@swarmvaultai/engine 0.5.0 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -21,7 +21,7 @@ import {
21
21
  uniqueBy,
22
22
  writeFileIfChanged,
23
23
  writeJsonFile
24
- } from "./chunk-B3FC4J3P.js";
24
+ } from "./chunk-OK5752AP.js";
25
25
 
26
26
  // src/agents.ts
27
27
  import crypto from "crypto";
@@ -8502,8 +8502,8 @@ async function readExtractionArtifact(rootDir, manifest) {
8502
8502
  }
8503
8503
 
8504
8504
  // src/mcp.ts
8505
- import fs19 from "fs/promises";
8506
- import path23 from "path";
8505
+ import fs20 from "fs/promises";
8506
+ import path24 from "path";
8507
8507
  import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
8508
8508
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
8509
8509
  import { z as z8 } from "zod";
@@ -8616,8 +8616,8 @@ function buildSchemaPrompt(schema, instruction) {
8616
8616
  }
8617
8617
 
8618
8618
  // src/vault.ts
8619
- import fs18 from "fs/promises";
8620
- import path22 from "path";
8619
+ import fs19 from "fs/promises";
8620
+ import path23 from "path";
8621
8621
  import matter9 from "gray-matter";
8622
8622
  import { z as z7 } from "zod";
8623
8623
 
@@ -10579,6 +10579,43 @@ import matter5 from "gray-matter";
10579
10579
  function uniqueStrings2(values) {
10580
10580
  return uniqueBy(values.filter(Boolean), (value) => value);
10581
10581
  }
10582
+ var GUIDED_SOURCE_MARKER_PREFIX = "<!-- swarmvault-guided-source:";
10583
+ var GUIDED_SOURCE_START_SUFFIX = ":start -->";
10584
+ var GUIDED_SOURCE_END_SUFFIX = ":end -->";
10585
+ function extractGuidedSourceBlocks(content) {
10586
+ if (!content) {
10587
+ return [];
10588
+ }
10589
+ const blocks = [];
10590
+ let cursor = 0;
10591
+ while (cursor < content.length) {
10592
+ const startIndex = content.indexOf(GUIDED_SOURCE_MARKER_PREFIX, cursor);
10593
+ if (startIndex === -1) {
10594
+ break;
10595
+ }
10596
+ const scopeEndIndex = content.indexOf(GUIDED_SOURCE_START_SUFFIX, startIndex);
10597
+ if (scopeEndIndex === -1) {
10598
+ break;
10599
+ }
10600
+ const scopeId = content.slice(startIndex + GUIDED_SOURCE_MARKER_PREFIX.length, scopeEndIndex);
10601
+ const endMarker = `${GUIDED_SOURCE_MARKER_PREFIX}${scopeId}${GUIDED_SOURCE_END_SUFFIX}`;
10602
+ const endIndex = content.indexOf(endMarker, scopeEndIndex);
10603
+ if (endIndex === -1) {
10604
+ break;
10605
+ }
10606
+ const blockEnd = endIndex + endMarker.length;
10607
+ blocks.push(content.slice(startIndex, blockEnd).trim());
10608
+ cursor = blockEnd;
10609
+ }
10610
+ return uniqueStrings2(blocks);
10611
+ }
10612
+ function appendGuidedSourceBlocks(body, existingContent) {
10613
+ const blocks = extractGuidedSourceBlocks(existingContent);
10614
+ if (!blocks.length) {
10615
+ return body;
10616
+ }
10617
+ return [body.trimEnd(), "", "## Guided Session Notes", "", ...blocks, ""].join("\n");
10618
+ }
10582
10619
  function safeFrontmatter(value) {
10583
10620
  return JSON.parse(JSON.stringify(value));
10584
10621
  }
@@ -10667,7 +10704,7 @@ function detailList(manifest, key) {
10667
10704
  const items = value.split(",").map((item) => item.trim()).filter(Boolean);
10668
10705
  return items.length ? items : void 0;
10669
10706
  }
10670
- function buildSourcePage(manifest, analysis, schemaHash, metadata, relatedOutputs = [], modulePage, decorations) {
10707
+ function buildSourcePage(manifest, analysis, schemaHash, metadata, relatedOutputs = [], modulePage, decorations, existingContent) {
10671
10708
  const relativePath = pagePathFor("source", manifest.sourceId);
10672
10709
  const pageId = `source:${manifest.sourceId}`;
10673
10710
  const { sourceHashes, sourceSemanticHashes } = sourceHashesForManifest(manifest);
@@ -10709,61 +10746,64 @@ function buildSourcePage(manifest, analysis, schemaHash, metadata, relatedOutput
10709
10746
  schema_hash: schemaHash,
10710
10747
  ...sourceHashFrontmatter(sourceHashes, sourceSemanticHashes)
10711
10748
  };
10712
- const body = [
10713
- `# ${analysis.title}`,
10714
- "",
10715
- `Source ID: \`${manifest.sourceId}\``,
10716
- `Source Kind: \`${manifest.sourceKind}\``,
10717
- manifest.url ? `Source URL: ${manifest.url}` : `Source Path: \`${manifest.originalPath ?? manifest.storedPath}\``,
10718
- ...manifest.sourceType ? [`Source Type: \`${manifest.sourceType}\``, ""] : [""],
10719
- ...manifest.sourceClass ? [`Source Class: \`${manifest.sourceClass}\``, ""] : [],
10720
- ...manifest.sourceGroupTitle ? [`Source Group: ${manifest.sourceGroupTitle}`] : [],
10721
- ...manifest.partTitle ? [`Part: ${manifest.partIndex ?? "?"}/${manifest.partCount ?? "?"} - ${manifest.partTitle}`] : [],
10722
- ...manifest.details && Object.keys(manifest.details).length ? [
10749
+ const body = appendGuidedSourceBlocks(
10750
+ [
10751
+ `# ${analysis.title}`,
10752
+ "",
10753
+ `Source ID: \`${manifest.sourceId}\``,
10754
+ `Source Kind: \`${manifest.sourceKind}\``,
10755
+ manifest.url ? `Source URL: ${manifest.url}` : `Source Path: \`${manifest.originalPath ?? manifest.storedPath}\``,
10756
+ ...manifest.sourceType ? [`Source Type: \`${manifest.sourceType}\``, ""] : [""],
10757
+ ...manifest.sourceClass ? [`Source Class: \`${manifest.sourceClass}\``, ""] : [],
10758
+ ...manifest.sourceGroupTitle ? [`Source Group: ${manifest.sourceGroupTitle}`] : [],
10759
+ ...manifest.partTitle ? [`Part: ${manifest.partIndex ?? "?"}/${manifest.partCount ?? "?"} - ${manifest.partTitle}`] : [],
10760
+ ...manifest.details && Object.keys(manifest.details).length ? [
10761
+ "",
10762
+ "## Source Details",
10763
+ "",
10764
+ ...Object.entries(manifest.details).map(([key, value]) => `- ${key.replace(/_/g, " ")}: ${value}`),
10765
+ ""
10766
+ ] : [],
10723
10767
  "",
10724
- "## Source Details",
10768
+ "## Summary",
10725
10769
  "",
10726
- ...Object.entries(manifest.details).map(([key, value]) => `- ${key.replace(/_/g, " ")}: ${value}`),
10727
- ""
10728
- ] : [],
10729
- "",
10730
- "## Summary",
10731
- "",
10732
- analysis.summary,
10733
- "",
10734
- ...analysis.code ? [
10735
- "## Code Module",
10770
+ analysis.summary,
10771
+ "",
10772
+ ...analysis.code ? [
10773
+ "## Code Module",
10774
+ "",
10775
+ `- Language: \`${analysis.code.language}\``,
10776
+ modulePage ? `- Module Page: [[${modulePage.path.replace(/\.md$/, "")}|${modulePage.title}]]` : "- Module Page: Not generated.",
10777
+ `- Exports: ${analysis.code.exports.length ? analysis.code.exports.join(", ") : "None detected."}`,
10778
+ `- Symbols: ${analysis.code.symbols.length ? analysis.code.symbols.map((symbol) => symbol.name).join(", ") : "None detected."}`,
10779
+ analysis.code.diagnostics.length ? `- Diagnostics: ${analysis.code.diagnostics.length}` : "- Diagnostics: None.",
10780
+ ""
10781
+ ] : [],
10782
+ "## Concepts",
10736
10783
  "",
10737
- `- Language: \`${analysis.code.language}\``,
10738
- modulePage ? `- Module Page: [[${modulePage.path.replace(/\.md$/, "")}|${modulePage.title}]]` : "- Module Page: Not generated.",
10739
- `- Exports: ${analysis.code.exports.length ? analysis.code.exports.join(", ") : "None detected."}`,
10740
- `- Symbols: ${analysis.code.symbols.length ? analysis.code.symbols.map((symbol) => symbol.name).join(", ") : "None detected."}`,
10741
- analysis.code.diagnostics.length ? `- Diagnostics: ${analysis.code.diagnostics.length}` : "- Diagnostics: None.",
10784
+ ...analysis.concepts.length ? analysis.concepts.map(
10785
+ (item) => `- [[${pagePathFor("concept", slugify(item.name)).replace(/\.md$/, "")}|${item.name}]]: ${item.description}`
10786
+ ) : ["- None detected."],
10787
+ "",
10788
+ "## Entities",
10789
+ "",
10790
+ ...analysis.entities.length ? analysis.entities.map(
10791
+ (item) => `- [[${pagePathFor("entity", slugify(item.name)).replace(/\.md$/, "")}|${item.name}]]: ${item.description}`
10792
+ ) : ["- None detected."],
10793
+ "",
10794
+ "## Claims",
10795
+ "",
10796
+ ...analysis.claims.length ? analysis.claims.map((claim) => `- ${claim.text} [source:${claim.citation}]`) : ["- No claims extracted."],
10797
+ "",
10798
+ "## Questions",
10799
+ "",
10800
+ ...analysis.questions.length ? analysis.questions.map((question) => `- ${question}`) : ["- No follow-up questions yet."],
10801
+ "",
10802
+ ...relatedOutputsSection(relatedOutputs),
10742
10803
  ""
10743
- ] : [],
10744
- "## Concepts",
10745
- "",
10746
- ...analysis.concepts.length ? analysis.concepts.map(
10747
- (item) => `- [[${pagePathFor("concept", slugify(item.name)).replace(/\.md$/, "")}|${item.name}]]: ${item.description}`
10748
- ) : ["- None detected."],
10749
- "",
10750
- "## Entities",
10751
- "",
10752
- ...analysis.entities.length ? analysis.entities.map(
10753
- (item) => `- [[${pagePathFor("entity", slugify(item.name)).replace(/\.md$/, "")}|${item.name}]]: ${item.description}`
10754
- ) : ["- None detected."],
10755
- "",
10756
- "## Claims",
10757
- "",
10758
- ...analysis.claims.length ? analysis.claims.map((claim) => `- ${claim.text} [source:${claim.citation}]`) : ["- No claims extracted."],
10759
- "",
10760
- "## Questions",
10761
- "",
10762
- ...analysis.questions.length ? analysis.questions.map((question) => `- ${question}`) : ["- No follow-up questions yet."],
10763
- "",
10764
- ...relatedOutputsSection(relatedOutputs),
10765
- ""
10766
- ].join("\n");
10804
+ ].join("\n"),
10805
+ existingContent
10806
+ );
10767
10807
  return {
10768
10808
  page: {
10769
10809
  id: pageId,
@@ -10943,7 +10983,7 @@ function buildModulePage(input) {
10943
10983
  content: matter5.stringify(body, frontmatter)
10944
10984
  };
10945
10985
  }
10946
- function buildAggregatePage(kind, name, descriptions, sourceAnalyses, sourceHashes, sourceSemanticHashes, schemaHash, metadata, relativePath, relatedOutputs = [], decorations) {
10986
+ function buildAggregatePage(kind, name, descriptions, sourceAnalyses, sourceHashes, sourceSemanticHashes, schemaHash, metadata, relativePath, relatedOutputs = [], decorations, existingContent) {
10947
10987
  const slug = slugify(name);
10948
10988
  const pageId = `${kind}:${slug}`;
10949
10989
  const sourceIds = sourceAnalyses.map((item) => item.sourceId);
@@ -10969,26 +11009,29 @@ function buildAggregatePage(kind, name, descriptions, sourceAnalyses, sourceHash
10969
11009
  schema_hash: schemaHash,
10970
11010
  ...sourceHashFrontmatter(sourceHashes, sourceSemanticHashes)
10971
11011
  };
10972
- const body = [
10973
- `# ${name}`,
10974
- "",
10975
- "## Summary",
10976
- "",
10977
- summary,
10978
- "",
10979
- "## Seen In",
10980
- "",
10981
- ...sourceAnalyses.map((item) => `- [[${pagePathFor("source", item.sourceId).replace(/\.md$/, "")}|${item.title}]]`),
10982
- "",
10983
- "## Source Claims",
10984
- "",
10985
- ...sourceAnalyses.flatMap(
10986
- (item) => item.claims.filter((claim) => claim.text.toLowerCase().includes(name.toLowerCase())).map((claim) => `- ${claim.text} [source:${claim.citation}]`)
10987
- ),
10988
- "",
10989
- ...relatedOutputsSection(relatedOutputs),
10990
- ""
10991
- ].join("\n");
11012
+ const body = appendGuidedSourceBlocks(
11013
+ [
11014
+ `# ${name}`,
11015
+ "",
11016
+ "## Summary",
11017
+ "",
11018
+ summary,
11019
+ "",
11020
+ "## Seen In",
11021
+ "",
11022
+ ...sourceAnalyses.map((item) => `- [[${pagePathFor("source", item.sourceId).replace(/\.md$/, "")}|${item.title}]]`),
11023
+ "",
11024
+ "## Source Claims",
11025
+ "",
11026
+ ...sourceAnalyses.flatMap(
11027
+ (item) => item.claims.filter((claim) => claim.text.toLowerCase().includes(name.toLowerCase())).map((claim) => `- ${claim.text} [source:${claim.citation}]`)
11028
+ ),
11029
+ "",
11030
+ ...relatedOutputsSection(relatedOutputs),
11031
+ ""
11032
+ ].join("\n"),
11033
+ existingContent
11034
+ );
10992
11035
  return {
10993
11036
  page: {
10994
11037
  id: pageId,
@@ -11137,15 +11180,15 @@ function sourceTypeForNode(node, pagesById) {
11137
11180
  return pagesById.get(node.pageId)?.sourceType;
11138
11181
  }
11139
11182
  function supportingPathDetails(graph, edge) {
11140
- const path28 = shortestGraphPath(graph, edge.source, edge.target);
11183
+ const path29 = shortestGraphPath(graph, edge.source, edge.target);
11141
11184
  const edgesById = new Map(graph.edges.map((item) => [item.id, item]));
11142
- const pathEdges = path28.edgeIds.map((edgeId) => edgesById.get(edgeId)).filter((item) => Boolean(item));
11185
+ const pathEdges = path29.edgeIds.map((edgeId) => edgesById.get(edgeId)).filter((item) => Boolean(item));
11143
11186
  return {
11144
- pathNodeIds: path28.nodeIds,
11145
- pathEdgeIds: path28.edgeIds,
11187
+ pathNodeIds: path29.nodeIds,
11188
+ pathEdgeIds: path29.edgeIds,
11146
11189
  pathRelations: pathEdges.map((item) => item.relation),
11147
11190
  pathEvidenceClasses: pathEdges.map((item) => item.evidenceClass),
11148
- pathSummary: path28.summary
11191
+ pathSummary: path29.summary
11149
11192
  };
11150
11193
  }
11151
11194
  function surpriseScore(edge, graph, pagesById, hyperedgesByNodeId) {
@@ -11214,7 +11257,7 @@ function topSurprisingConnections(graph, pagesById) {
11214
11257
  }).map((edge) => {
11215
11258
  const source = nodesById.get(edge.source);
11216
11259
  const target = nodesById.get(edge.target);
11217
- const path28 = supportingPathDetails(graph, edge);
11260
+ const path29 = supportingPathDetails(graph, edge);
11218
11261
  const scored = surpriseScore(edge, graph, pagesById, hyperedgesByNodeId);
11219
11262
  return {
11220
11263
  id: edge.id,
@@ -11225,11 +11268,11 @@ function topSurprisingConnections(graph, pagesById) {
11225
11268
  relation: edge.relation,
11226
11269
  evidenceClass: edge.evidenceClass,
11227
11270
  confidence: edge.confidence,
11228
- pathNodeIds: path28.pathNodeIds,
11229
- pathEdgeIds: path28.pathEdgeIds,
11230
- pathRelations: path28.pathRelations,
11231
- pathEvidenceClasses: path28.pathEvidenceClasses,
11232
- pathSummary: path28.pathSummary,
11271
+ pathNodeIds: path29.pathNodeIds,
11272
+ pathEdgeIds: path29.pathEdgeIds,
11273
+ pathRelations: path29.pathRelations,
11274
+ pathEvidenceClasses: path29.pathEvidenceClasses,
11275
+ pathSummary: path29.pathSummary,
11233
11276
  why: scored.why,
11234
11277
  explanation: scored.explanation,
11235
11278
  surpriseScore: scored.score
@@ -11744,7 +11787,8 @@ function buildOutputPage(input) {
11744
11787
  question: input.question,
11745
11788
  output_format: input.outputFormat,
11746
11789
  output_assets: outputAssets,
11747
- ...input.outputFormat === "slides" ? { marp: true } : {}
11790
+ ...input.outputFormat === "slides" ? { marp: true } : {},
11791
+ ...input.frontmatter ?? {}
11748
11792
  };
11749
11793
  return {
11750
11794
  page: {
@@ -11832,7 +11876,7 @@ function buildOutputPage(input) {
11832
11876
  ...input.citations.map((citation) => `- [source:${citation}]`),
11833
11877
  ""
11834
11878
  ]).join("\n"),
11835
- frontmatter
11879
+ safeFrontmatter(frontmatter)
11836
11880
  )
11837
11881
  };
11838
11882
  }
@@ -12691,6 +12735,58 @@ function searchPages(dbPath, query, limitOrOptions = 5) {
12691
12735
  }));
12692
12736
  }
12693
12737
 
12738
+ // src/source-sessions.ts
12739
+ import fs18 from "fs/promises";
12740
+ import path22 from "path";
12741
+ function sessionStatePathFor(paths, sessionId) {
12742
+ return path22.join(paths.sourceSessionsDir, `${sessionId}.json`);
12743
+ }
12744
+ async function listGuidedSourceSessions(rootDir) {
12745
+ const { paths } = await loadVaultConfig(rootDir);
12746
+ const entries = await fs18.readdir(paths.sourceSessionsDir, { withFileTypes: true }).catch(() => []);
12747
+ const sessions = await Promise.all(
12748
+ entries.filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map(async (entry) => await readJsonFile(path22.join(paths.sourceSessionsDir, entry.name)))
12749
+ );
12750
+ return sessions.filter((session) => Boolean(session)).sort((left, right) => right.updatedAt.localeCompare(left.updatedAt));
12751
+ }
12752
+ async function readGuidedSourceSession(rootDir, sessionId) {
12753
+ const { paths } = await loadVaultConfig(rootDir);
12754
+ return await readJsonFile(sessionStatePathFor(paths, sessionId));
12755
+ }
12756
+ async function findLatestGuidedSourceSessionByScope(rootDir, scopeId) {
12757
+ const sessions = await listGuidedSourceSessions(rootDir);
12758
+ return sessions.find((session) => session.scopeId === scopeId) ?? null;
12759
+ }
12760
+ async function writeGuidedSourceSession(rootDir, session) {
12761
+ const { paths } = await loadVaultConfig(rootDir);
12762
+ await ensureDir(paths.sourceSessionsDir);
12763
+ const next = {
12764
+ ...session,
12765
+ updatedAt: session.updatedAt || (/* @__PURE__ */ new Date()).toISOString()
12766
+ };
12767
+ const filePath = sessionStatePathFor(paths, session.sessionId);
12768
+ await fs18.writeFile(filePath, `${JSON.stringify(next, null, 2)}
12769
+ `, "utf8");
12770
+ return filePath;
12771
+ }
12772
+ async function updateGuidedSourceSessionStatus(rootDir, sessionId, status) {
12773
+ const existing = await readGuidedSourceSession(rootDir, sessionId);
12774
+ if (!existing) {
12775
+ return null;
12776
+ }
12777
+ const next = {
12778
+ ...existing,
12779
+ status,
12780
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
12781
+ };
12782
+ await writeGuidedSourceSession(rootDir, next);
12783
+ return next;
12784
+ }
12785
+ async function guidedSourceSessionStatePath(rootDir, sessionId) {
12786
+ const { paths } = await loadVaultConfig(rootDir);
12787
+ return sessionStatePathFor(paths, sessionId);
12788
+ }
12789
+
12694
12790
  // src/vault.ts
12695
12791
  var COMPILE_PROGRESS_THRESHOLD = 120;
12696
12792
  var COMPILE_PROGRESS_UPDATE_INTERVAL = 50;
@@ -12745,7 +12841,7 @@ function outputFormatInstruction(format) {
12745
12841
  }
12746
12842
  }
12747
12843
  function outputAssetPath(slug, fileName) {
12748
- return toPosix(path22.join("outputs", "assets", slug, fileName));
12844
+ return toPosix(path23.join("outputs", "assets", slug, fileName));
12749
12845
  }
12750
12846
  function outputAssetId(slug, role) {
12751
12847
  return `output:${slug}:asset:${role}`;
@@ -12885,7 +12981,7 @@ async function resolveImageGenerationProvider(rootDir) {
12885
12981
  if (!providerConfig) {
12886
12982
  throw new Error(`No provider configured with id "${preferredProviderId}" for task "imageProvider".`);
12887
12983
  }
12888
- const { createProvider: createProvider2 } = await import("./registry-KVJAO5DF.js");
12984
+ const { createProvider: createProvider2 } = await import("./registry-TYROWPR5.js");
12889
12985
  return createProvider2(preferredProviderId, providerConfig, rootDir);
12890
12986
  }
12891
12987
  async function generateOutputArtifacts(rootDir, input) {
@@ -13083,7 +13179,7 @@ async function generateOutputArtifacts(rootDir, input) {
13083
13179
  };
13084
13180
  }
13085
13181
  function normalizeProjectRoot(root) {
13086
- const normalized = toPosix(path22.posix.normalize(root.replace(/\\/g, "/"))).replace(/^\.\/+/, "").replace(/\/+$/, "");
13182
+ const normalized = toPosix(path23.posix.normalize(root.replace(/\\/g, "/"))).replace(/^\.\/+/, "").replace(/\/+$/, "");
13087
13183
  return normalized;
13088
13184
  }
13089
13185
  function projectEntries(config) {
@@ -13109,10 +13205,10 @@ function manifestPathForProject(rootDir, manifest) {
13109
13205
  if (!rawPath) {
13110
13206
  return toPosix(manifest.storedPath);
13111
13207
  }
13112
- if (!path22.isAbsolute(rawPath)) {
13208
+ if (!path23.isAbsolute(rawPath)) {
13113
13209
  return normalizeProjectRoot(rawPath);
13114
13210
  }
13115
- const relative = toPosix(path22.relative(rootDir, rawPath));
13211
+ const relative = toPosix(path23.relative(rootDir, rawPath));
13116
13212
  return relative.startsWith("..") ? toPosix(rawPath) : normalizeProjectRoot(relative);
13117
13213
  }
13118
13214
  function prefixMatches(value, prefix) {
@@ -13265,6 +13361,7 @@ function approvalSummary(manifest) {
13265
13361
  createdAt: manifest.createdAt,
13266
13362
  bundleType: manifest.bundleType,
13267
13363
  title: manifest.title,
13364
+ sourceSessionId: manifest.sourceSessionId,
13268
13365
  entryCount: manifest.entries.length,
13269
13366
  pendingCount: manifest.entries.filter((entry) => entry.status === "pending").length,
13270
13367
  acceptedCount: manifest.entries.filter((entry) => entry.status === "accepted").length,
@@ -13288,7 +13385,8 @@ function pageHashes(pages) {
13288
13385
  return Object.fromEntries(pages.map((page) => [page.page.id, page.contentHash]));
13289
13386
  }
13290
13387
  async function buildManagedGraphPage(absolutePath, defaults, build) {
13291
- const existingContent = await fileExists(absolutePath) ? await fs18.readFile(absolutePath, "utf8") : null;
13388
+ const existingContent = await fileExists(absolutePath) ? await fs19.readFile(absolutePath, "utf8") : null;
13389
+ let carriedContent = existingContent;
13292
13390
  let existing = await loadExistingManagedPageState(absolutePath, {
13293
13391
  status: defaults.status ?? "active",
13294
13392
  managedBy: defaults.managedBy
@@ -13303,6 +13401,7 @@ async function buildManagedGraphPage(absolutePath, defaults, build) {
13303
13401
  status: defaults.status ?? "active",
13304
13402
  managedBy: defaults.managedBy
13305
13403
  });
13404
+ carriedContent = await fs19.readFile(candidatePath, "utf8");
13306
13405
  usedFallbackState = true;
13307
13406
  break;
13308
13407
  }
@@ -13315,18 +13414,18 @@ async function buildManagedGraphPage(absolutePath, defaults, build) {
13315
13414
  managedBy: defaults.managedBy,
13316
13415
  confidence: defaults.confidence
13317
13416
  };
13318
- let built = build(metadata);
13319
- if (existingContent && existingContent !== built.content) {
13417
+ let built = build(metadata, carriedContent);
13418
+ if (carriedContent && carriedContent !== built.content) {
13320
13419
  metadata = {
13321
13420
  ...metadata,
13322
13421
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
13323
13422
  };
13324
- built = build(metadata);
13423
+ built = build(metadata, carriedContent);
13325
13424
  }
13326
13425
  return built;
13327
13426
  }
13328
13427
  async function buildManagedContent(absolutePath, defaults, build) {
13329
- const existingContent = await fileExists(absolutePath) ? await fs18.readFile(absolutePath, "utf8") : null;
13428
+ const existingContent = await fileExists(absolutePath) ? await fs19.readFile(absolutePath, "utf8") : null;
13330
13429
  let existing = await loadExistingManagedPageState(absolutePath, {
13331
13430
  status: defaults.status ?? "active",
13332
13431
  managedBy: defaults.managedBy
@@ -13368,15 +13467,19 @@ function manifestDetailValue(manifest, key) {
13368
13467
  }
13369
13468
  async function loadAnalysesBySourceIds(paths, sourceIds) {
13370
13469
  const analyses = await Promise.all(
13371
- sourceIds.map(async (sourceId) => await readJsonFile(path22.join(paths.analysesDir, `${sourceId}.json`)))
13470
+ sourceIds.map(async (sourceId) => await readJsonFile(path23.join(paths.analysesDir, `${sourceId}.json`)))
13372
13471
  );
13373
13472
  return analyses.filter((analysis) => Boolean(analysis?.sourceId));
13374
13473
  }
13375
- async function buildDashboardRecords(paths, graph, schemaHash, report) {
13474
+ async function buildDashboardRecords(config, paths, graph, schemaHash, report) {
13475
+ const dataviewEnabled = config.profile.dataviewBlocks;
13476
+ const profilePresets = config.profile.presets;
13477
+ const dashboardPack = config.profile.dashboardPack;
13376
13478
  const sourcePages = graph.pages.filter((page) => page.kind === "source");
13377
13479
  const reviewPages = graph.pages.filter((page) => page.kind === "output" && page.path.startsWith("outputs/source-reviews/"));
13378
13480
  const briefPages = graph.pages.filter((page) => page.kind === "output" && page.path.startsWith("outputs/source-briefs/"));
13379
13481
  const guidePages = graph.pages.filter((page) => page.kind === "output" && page.path.startsWith("outputs/source-guides/"));
13482
+ const sessionPages = graph.pages.filter((page) => page.kind === "output" && page.path.startsWith("outputs/source-sessions/"));
13380
13483
  const conceptPages = graph.pages.filter((page) => page.kind === "concept" && page.status !== "candidate").slice(0, 16);
13381
13484
  const entityPages = graph.pages.filter((page) => page.kind === "entity" && page.status !== "candidate").slice(0, 16);
13382
13485
  const manifests = graph.sources;
@@ -13387,9 +13490,12 @@ async function buildDashboardRecords(paths, graph, schemaHash, report) {
13387
13490
  const openQuestions = uniqueStrings3(
13388
13491
  analyses.flatMap((analysis) => analysis.questions.map((question) => `${analysis.title}: ${question}`))
13389
13492
  ).slice(0, 20);
13493
+ const sourceSessions = await listGuidedSourceSessions(paths.rootDir);
13390
13494
  const stagedGuideBundles = (await Promise.all(
13391
- (await fs18.readdir(paths.approvalsDir, { withFileTypes: true }).catch(() => [])).filter((entry) => entry.isDirectory()).map(async (entry) => await readJsonFile(approvalManifestPath(paths, entry.name)))
13392
- )).filter((manifest) => Boolean(manifest)).filter((manifest) => manifest.bundleType === "guided_source").sort((left, right) => right.createdAt.localeCompare(left.createdAt)).slice(0, 12);
13495
+ (await fs19.readdir(paths.approvalsDir, { withFileTypes: true }).catch(() => [])).filter((entry) => entry.isDirectory()).map(async (entry) => await readJsonFile(approvalManifestPath(paths, entry.name)))
13496
+ )).filter((manifest) => Boolean(manifest)).filter((manifest) => manifest.bundleType === "guided_source" || manifest.bundleType === "guided_session").sort((left, right) => right.createdAt.localeCompare(left.createdAt)).slice(0, 12);
13497
+ const readerFocusPages = uniqueBy([...guidePages, ...briefPages, ...conceptPages, ...entityPages], (page) => page.id).slice(0, 8);
13498
+ const diligenceSessions = sourceSessions.filter((session) => session.status === "staged" || session.status === "awaiting_input").slice(0, 8);
13393
13499
  const dashboards = [
13394
13500
  {
13395
13501
  relativePath: "dashboards/index.md",
@@ -13401,17 +13507,23 @@ async function buildDashboardRecords(paths, graph, schemaHash, report) {
13401
13507
  "- [[dashboards/recent-sources|Recent Sources]]",
13402
13508
  "- [[dashboards/reading-log|Reading Log]]",
13403
13509
  "- [[dashboards/timeline|Timeline]]",
13510
+ "- [[dashboards/source-sessions|Source Sessions]]",
13404
13511
  "- [[dashboards/source-guides|Source Guides]]",
13405
13512
  "- [[dashboards/research-map|Research Map]]",
13406
13513
  "- [[dashboards/contradictions|Contradictions]]",
13407
13514
  "- [[dashboards/open-questions|Open Questions]]",
13408
13515
  "",
13409
- "```dataview",
13410
- "TABLE file.mtime AS updated",
13411
- 'FROM "dashboards"',
13412
- 'WHERE file.name != "index"',
13413
- "SORT file.mtime desc",
13414
- "```",
13516
+ `Profile Presets: ${profilePresets.length ? profilePresets.map((preset) => `\`${preset}\``).join(", ") : "_default_"}`,
13517
+ `Dashboard Pack: \`${dashboardPack}\``,
13518
+ ...dataviewEnabled ? [
13519
+ "",
13520
+ "```dataview",
13521
+ "TABLE file.mtime AS updated",
13522
+ 'FROM "dashboards"',
13523
+ 'WHERE file.name != "index"',
13524
+ "SORT file.mtime desc",
13525
+ "```"
13526
+ ] : [],
13415
13527
  ""
13416
13528
  ].join("\n"),
13417
13529
  {
@@ -13432,7 +13544,8 @@ async function buildDashboardRecords(paths, graph, schemaHash, report) {
13432
13544
  backlinks: [],
13433
13545
  schema_hash: schemaHash,
13434
13546
  source_hashes: {},
13435
- source_semantic_hashes: {}
13547
+ source_semantic_hashes: {},
13548
+ profile_presets: profilePresets
13436
13549
  }
13437
13550
  )
13438
13551
  },
@@ -13444,13 +13557,16 @@ async function buildDashboardRecords(paths, graph, schemaHash, report) {
13444
13557
  "# Recent Sources",
13445
13558
  "",
13446
13559
  ...recentSourcePages.length ? recentSourcePages.map((page) => `- ${page.updatedAt}: [[${page.path.replace(/\.md$/, "")}|${page.title}]]`) : ["- No source pages yet."],
13447
- "",
13448
- "```dataview",
13449
- "TABLE source_type, occurred_at, participants",
13450
- 'FROM "sources"',
13451
- "SORT updated_at desc",
13452
- "LIMIT 25",
13453
- "```",
13560
+ ...dashboardPack === "reader" && readerFocusPages.length ? ["", "## Reader Focus", "", ...readerFocusPages.map((page) => `- [[${page.path.replace(/\.md$/, "")}|${page.title}]]`)] : [],
13561
+ ...dataviewEnabled ? [
13562
+ "",
13563
+ "```dataview",
13564
+ "TABLE source_type, occurred_at, participants",
13565
+ 'FROM "sources"',
13566
+ "SORT updated_at desc",
13567
+ "LIMIT 25",
13568
+ "```"
13569
+ ] : [],
13454
13570
  ""
13455
13571
  ].join("\n"),
13456
13572
  {
@@ -13471,7 +13587,8 @@ async function buildDashboardRecords(paths, graph, schemaHash, report) {
13471
13587
  backlinks: [],
13472
13588
  schema_hash: schemaHash,
13473
13589
  source_hashes: {},
13474
- source_semantic_hashes: {}
13590
+ source_semantic_hashes: {},
13591
+ profile_presets: profilePresets
13475
13592
  }
13476
13593
  )
13477
13594
  },
@@ -13487,13 +13604,29 @@ async function buildDashboardRecords(paths, graph, schemaHash, report) {
13487
13604
  const participants = manifestDetailValue(manifest, "participants");
13488
13605
  return `- ${occurredAt}: ${manifest.title}${participants ? ` (${participants})` : ""}`;
13489
13606
  }) : recentSourcePages.map((page) => `- ${page.updatedAt}: [[${page.path.replace(/\.md$/, "")}|${page.title}]]`),
13490
- "",
13491
- "```dataview",
13492
- "TABLE occurred_at, source_type, participants, container_title",
13493
- 'FROM "sources"',
13494
- "SORT occurred_at desc",
13495
- "LIMIT 25",
13496
- "```",
13607
+ ...sourceSessions.length ? [
13608
+ "",
13609
+ "## Active Guided Sessions",
13610
+ "",
13611
+ ...sourceSessions.slice(0, 8).map(
13612
+ (session) => `- ${session.updatedAt}: \`${session.status}\` [[outputs/source-sessions/${session.scopeId}|${session.scopeTitle}]]`
13613
+ )
13614
+ ] : [],
13615
+ ...dashboardPack === "reader" && conceptPages.length ? [
13616
+ "",
13617
+ "## Thesis And Hub Pages",
13618
+ "",
13619
+ ...conceptPages.slice(0, 6).map((page) => `- [[${page.path.replace(/\.md$/, "")}|${page.title}]]`)
13620
+ ] : [],
13621
+ ...dataviewEnabled ? [
13622
+ "",
13623
+ "```dataview",
13624
+ "TABLE occurred_at, source_type, participants, container_title",
13625
+ 'FROM "sources"',
13626
+ "SORT occurred_at desc",
13627
+ "LIMIT 25",
13628
+ "```"
13629
+ ] : [],
13497
13630
  ""
13498
13631
  ].join("\n"),
13499
13632
  {
@@ -13514,7 +13647,8 @@ async function buildDashboardRecords(paths, graph, schemaHash, report) {
13514
13647
  backlinks: [],
13515
13648
  schema_hash: schemaHash,
13516
13649
  source_hashes: {},
13517
- source_semantic_hashes: {}
13650
+ source_semantic_hashes: {},
13651
+ profile_presets: profilePresets
13518
13652
  }
13519
13653
  )
13520
13654
  },
@@ -13530,13 +13664,15 @@ async function buildDashboardRecords(paths, graph, schemaHash, report) {
13530
13664
  const sourcePage = sourcePages.find((page) => page.sourceIds.includes(manifest.sourceId));
13531
13665
  return `- ${occurredAt}: ${sourcePage ? `[[${sourcePage.path.replace(/\.md$/, "")}|${sourcePage.title}]]` : manifest.title}`;
13532
13666
  }) : ["- No timeline-aware sources yet."],
13533
- "",
13534
- "```dataview",
13535
- "TABLE occurred_at, participants, container_title",
13536
- 'FROM "sources"',
13537
- "WHERE occurred_at",
13538
- "SORT occurred_at desc",
13539
- "```",
13667
+ ...dataviewEnabled ? [
13668
+ "",
13669
+ "```dataview",
13670
+ "TABLE occurred_at, participants, container_title",
13671
+ 'FROM "sources"',
13672
+ "WHERE occurred_at",
13673
+ "SORT occurred_at desc",
13674
+ "```"
13675
+ ] : [],
13540
13676
  ""
13541
13677
  ].join("\n"),
13542
13678
  {
@@ -13557,7 +13693,65 @@ async function buildDashboardRecords(paths, graph, schemaHash, report) {
13557
13693
  backlinks: [],
13558
13694
  schema_hash: schemaHash,
13559
13695
  source_hashes: {},
13560
- source_semantic_hashes: {}
13696
+ source_semantic_hashes: {},
13697
+ profile_presets: profilePresets
13698
+ }
13699
+ )
13700
+ },
13701
+ {
13702
+ relativePath: "dashboards/source-sessions.md",
13703
+ title: "Source Sessions",
13704
+ content: (metadata) => matter9.stringify(
13705
+ [
13706
+ "# Source Sessions",
13707
+ "",
13708
+ "## Active Sessions",
13709
+ "",
13710
+ ...sourceSessions.length ? sourceSessions.slice(0, 16).map(
13711
+ (session) => `- ${session.updatedAt}: \`${session.status}\` \`${session.sessionId}\` [[outputs/source-sessions/${session.scopeId}|${session.scopeTitle}]]`
13712
+ ) : ["- No guided source sessions yet."],
13713
+ "",
13714
+ "## Pending Guided Bundles",
13715
+ "",
13716
+ ...stagedGuideBundles.length ? stagedGuideBundles.map(
13717
+ (bundle) => `- ${bundle.createdAt}: \`${bundle.approvalId}\`${bundle.title ? ` ${bundle.title}` : ""} (${bundle.entries.length} staged entr${bundle.entries.length === 1 ? "y" : "ies"})`
13718
+ ) : ["- No staged guided bundles right now."],
13719
+ ...dataviewEnabled ? [
13720
+ "",
13721
+ "```dataview",
13722
+ "TABLE session_status, evidence_state, canonical_targets",
13723
+ 'FROM "outputs/source-sessions"',
13724
+ "SORT file.mtime desc",
13725
+ "```"
13726
+ ] : [],
13727
+ ""
13728
+ ].join("\n"),
13729
+ {
13730
+ page_id: "dashboards:source-sessions",
13731
+ kind: "index",
13732
+ title: "Source Sessions",
13733
+ tags: ["index", "dashboard", "source-sessions"],
13734
+ source_ids: uniqueStrings3([
13735
+ ...sessionPages.flatMap((page) => page.sourceIds),
13736
+ ...sourceSessions.flatMap((session) => session.sourceIds)
13737
+ ]),
13738
+ project_ids: [],
13739
+ node_ids: [],
13740
+ freshness: "fresh",
13741
+ status: metadata.status,
13742
+ confidence: 1,
13743
+ created_at: metadata.createdAt,
13744
+ updated_at: metadata.updatedAt,
13745
+ compiled_from: uniqueStrings3([
13746
+ ...sessionPages.flatMap((page) => page.sourceIds),
13747
+ ...sourceSessions.flatMap((session) => session.sourceIds)
13748
+ ]),
13749
+ managed_by: metadata.managedBy,
13750
+ backlinks: [],
13751
+ schema_hash: schemaHash,
13752
+ source_hashes: {},
13753
+ source_semantic_hashes: {},
13754
+ profile_presets: profilePresets
13561
13755
  }
13562
13756
  )
13563
13757
  },
@@ -13575,11 +13769,14 @@ async function buildDashboardRecords(paths, graph, schemaHash, report) {
13575
13769
  ...stagedGuideBundles.length ? stagedGuideBundles.map(
13576
13770
  (bundle) => `- ${bundle.createdAt}: \`${bundle.approvalId}\`${bundle.title ? ` ${bundle.title}` : ""} (${bundle.entries.length} staged entr${bundle.entries.length === 1 ? "y" : "ies"})`
13577
13771
  ) : ["- No staged guided bundles right now."],
13578
- "",
13579
- "```dataview",
13580
- 'LIST FROM "outputs/source-guides"',
13581
- "SORT file.mtime desc",
13582
- "```",
13772
+ ...dataviewEnabled ? [
13773
+ "",
13774
+ "```dataview",
13775
+ "TABLE evidence_state, canonical_targets, file.mtime AS updated",
13776
+ 'FROM "outputs/source-guides"',
13777
+ "SORT file.mtime desc",
13778
+ "```"
13779
+ ] : [],
13583
13780
  ""
13584
13781
  ].join("\n"),
13585
13782
  {
@@ -13606,7 +13803,8 @@ async function buildDashboardRecords(paths, graph, schemaHash, report) {
13606
13803
  backlinks: [],
13607
13804
  schema_hash: schemaHash,
13608
13805
  source_hashes: {},
13609
- source_semantic_hashes: {}
13806
+ source_semantic_hashes: {},
13807
+ profile_presets: profilePresets
13610
13808
  }
13611
13809
  )
13612
13810
  },
@@ -13628,13 +13826,19 @@ async function buildDashboardRecords(paths, graph, schemaHash, report) {
13628
13826
  "## Recently Guided Sources",
13629
13827
  "",
13630
13828
  ...guidePages.length ? guidePages.slice(0, 8).map((page) => `- [[${page.path.replace(/\.md$/, "")}|${page.title}]]`) : ["- No accepted source guides yet."],
13631
- ...report?.suggestedQuestions?.length ? ["", "## Suggested Questions", "", ...report.suggestedQuestions.slice(0, 8).map((question) => `- ${question}`)] : [],
13632
13829
  "",
13633
- "```dataview",
13634
- 'TABLE file.folder, file.mtime FROM "concepts" OR "entities"',
13635
- "SORT file.mtime desc",
13636
- "LIMIT 30",
13637
- "```",
13830
+ "## Active Source Sessions",
13831
+ "",
13832
+ ...sourceSessions.length ? sourceSessions.slice(0, 8).map((session) => `- \`${session.status}\` [[outputs/source-sessions/${session.scopeId}|${session.scopeTitle}]]`) : ["- No active source sessions yet."],
13833
+ ...report?.suggestedQuestions?.length ? ["", "## Suggested Questions", "", ...report.suggestedQuestions.slice(0, 8).map((question) => `- ${question}`)] : [],
13834
+ ...dataviewEnabled ? [
13835
+ "",
13836
+ "```dataview",
13837
+ 'TABLE file.folder, file.mtime FROM "concepts" OR "entities"',
13838
+ "SORT file.mtime desc",
13839
+ "LIMIT 30",
13840
+ "```"
13841
+ ] : [],
13638
13842
  ""
13639
13843
  ].join("\n"),
13640
13844
  {
@@ -13663,7 +13867,8 @@ async function buildDashboardRecords(paths, graph, schemaHash, report) {
13663
13867
  backlinks: [],
13664
13868
  schema_hash: schemaHash,
13665
13869
  source_hashes: {},
13666
- source_semantic_hashes: {}
13870
+ source_semantic_hashes: {},
13871
+ profile_presets: profilePresets
13667
13872
  }
13668
13873
  )
13669
13874
  },
@@ -13686,10 +13891,21 @@ async function buildDashboardRecords(paths, graph, schemaHash, report) {
13686
13891
  ...[...guidePages, ...reviewPages, ...briefPages].slice(0, 12).map((page) => `- [[${page.path.replace(/\.md$/, "")}|${page.title}]]`),
13687
13892
  ""
13688
13893
  ] : [],
13689
- "```dataview",
13690
- 'LIST FROM "outputs/source-reviews" OR "outputs/source-guides"',
13691
- "SORT file.mtime desc",
13692
- "```",
13894
+ ...dashboardPack === "diligence" && diligenceSessions.length ? [
13895
+ "## Active Evidence Review Sessions",
13896
+ "",
13897
+ ...diligenceSessions.map(
13898
+ (session) => `- \`${session.status}\` [[outputs/source-sessions/${session.scopeId}|${session.scopeTitle}]]`
13899
+ ),
13900
+ ""
13901
+ ] : [],
13902
+ ...dataviewEnabled ? [
13903
+ "```dataview",
13904
+ 'TABLE evidence_state, session_status, canonical_targets FROM "outputs/source-reviews" OR "outputs/source-guides" OR "outputs/source-sessions"',
13905
+ 'WHERE evidence_state = "conflicting"',
13906
+ "SORT file.mtime desc",
13907
+ "```"
13908
+ ] : [],
13693
13909
  ""
13694
13910
  ].join("\n"),
13695
13911
  {
@@ -13710,7 +13926,8 @@ async function buildDashboardRecords(paths, graph, schemaHash, report) {
13710
13926
  backlinks: [],
13711
13927
  schema_hash: schemaHash,
13712
13928
  source_hashes: {},
13713
- source_semantic_hashes: {}
13929
+ source_semantic_hashes: {},
13930
+ profile_presets: profilePresets
13714
13931
  }
13715
13932
  )
13716
13933
  },
@@ -13722,11 +13939,19 @@ async function buildDashboardRecords(paths, graph, schemaHash, report) {
13722
13939
  "# Open Questions",
13723
13940
  "",
13724
13941
  ...openQuestions.length ? openQuestions.map((question) => `- ${question}`) : ["- No open questions are currently extracted."],
13725
- "",
13726
- "```dataview",
13727
- 'LIST FROM "outputs/source-briefs" OR "outputs/source-reviews" OR "outputs/source-guides"',
13728
- "SORT file.mtime desc",
13729
- "```",
13942
+ ...sourceSessions.length ? [
13943
+ "",
13944
+ "## Active Guided Sessions",
13945
+ "",
13946
+ ...sourceSessions.filter((session) => session.status === "awaiting_input" || session.status === "staged").slice(0, 8).map((session) => `- \`${session.status}\` [[outputs/source-sessions/${session.scopeId}|${session.scopeTitle}]]`)
13947
+ ] : [],
13948
+ ...dataviewEnabled ? [
13949
+ "",
13950
+ "```dataview",
13951
+ 'TABLE question_state, session_status, evidence_state FROM "outputs/source-briefs" OR "outputs/source-reviews" OR "outputs/source-guides" OR "outputs/source-sessions"',
13952
+ "SORT file.mtime desc",
13953
+ "```"
13954
+ ] : [],
13730
13955
  ""
13731
13956
  ].join("\n"),
13732
13957
  {
@@ -13747,14 +13972,15 @@ async function buildDashboardRecords(paths, graph, schemaHash, report) {
13747
13972
  backlinks: [],
13748
13973
  schema_hash: schemaHash,
13749
13974
  source_hashes: {},
13750
- source_semantic_hashes: {}
13975
+ source_semantic_hashes: {},
13976
+ profile_presets: profilePresets
13751
13977
  }
13752
13978
  )
13753
13979
  }
13754
13980
  ];
13755
13981
  const records = [];
13756
13982
  for (const dashboard of dashboards) {
13757
- const absolutePath = path22.join(paths.wikiDir, dashboard.relativePath);
13983
+ const absolutePath = path23.join(paths.wikiDir, dashboard.relativePath);
13758
13984
  const compiledFrom = dashboard.relativePath === "dashboards/recent-sources.md" ? recentSourcePages.flatMap((page) => page.sourceIds) : [];
13759
13985
  const content = await buildManagedContent(
13760
13986
  absolutePath,
@@ -13868,7 +14094,7 @@ function resetGraphNodeMetrics(nodes) {
13868
14094
  return nodes.map(({ communityId: _communityId, degree: _degree, bridgeScore: _bridgeScore, isGodNode: _isGodNode, ...node }) => node);
13869
14095
  }
13870
14096
  function manifestRepoPath(manifest) {
13871
- return toPosix(manifest.repoRelativePath ?? path22.basename(manifest.originalPath ?? manifest.storedPath));
14097
+ return toPosix(manifest.repoRelativePath ?? path23.basename(manifest.originalPath ?? manifest.storedPath));
13872
14098
  }
13873
14099
  function goPackageScopeKey(manifest, analysis) {
13874
14100
  if (analysis.code?.language !== "go") {
@@ -13878,7 +14104,7 @@ function goPackageScopeKey(manifest, analysis) {
13878
14104
  if (!packageName) {
13879
14105
  return null;
13880
14106
  }
13881
- return `${packageName}:${path22.posix.dirname(manifestRepoPath(manifest))}`;
14107
+ return `${packageName}:${path23.posix.dirname(manifestRepoPath(manifest))}`;
13882
14108
  }
13883
14109
  function buildGoPackageSymbolLookups(analyses, manifestsById) {
13884
14110
  const lookups = /* @__PURE__ */ new Map();
@@ -14347,7 +14573,7 @@ async function buildGraphOrientationPages(graph, paths, schemaHash, previousComp
14347
14573
  const benchmark = await readJsonFile(paths.benchmarkPath);
14348
14574
  const communityRecords = [];
14349
14575
  for (const community of graph.communities ?? []) {
14350
- const absolutePath = path22.join(paths.wikiDir, "graph", "communities", `${community.id.replace(/^community:/, "")}.md`);
14576
+ const absolutePath = path23.join(paths.wikiDir, "graph", "communities", `${community.id.replace(/^community:/, "")}.md`);
14351
14577
  communityRecords.push(
14352
14578
  await buildManagedGraphPage(
14353
14579
  absolutePath,
@@ -14376,7 +14602,7 @@ async function buildGraphOrientationPages(graph, paths, schemaHash, previousComp
14376
14602
  graphHash: graphHash(graph),
14377
14603
  contradictions
14378
14604
  });
14379
- const reportAbsolutePath = path22.join(paths.wikiDir, "graph", "report.md");
14605
+ const reportAbsolutePath = path23.join(paths.wikiDir, "graph", "report.md");
14380
14606
  const reportRecord = await buildManagedGraphPage(
14381
14607
  reportAbsolutePath,
14382
14608
  {
@@ -14397,7 +14623,7 @@ async function buildGraphOrientationPages(graph, paths, schemaHash, previousComp
14397
14623
  };
14398
14624
  }
14399
14625
  async function writePage(wikiDir, relativePath, content, changedPages) {
14400
- const absolutePath = path22.resolve(wikiDir, relativePath);
14626
+ const absolutePath = path23.resolve(wikiDir, relativePath);
14401
14627
  const changed = await writeFileIfChanged(absolutePath, content);
14402
14628
  if (changed) {
14403
14629
  changedPages.push(relativePath);
@@ -14463,29 +14689,29 @@ async function requiredCompileArtifactsExist(paths) {
14463
14689
  paths.graphPath,
14464
14690
  paths.codeIndexPath,
14465
14691
  paths.searchDbPath,
14466
- path22.join(paths.wikiDir, "index.md"),
14467
- path22.join(paths.wikiDir, "sources", "index.md"),
14468
- path22.join(paths.wikiDir, "code", "index.md"),
14469
- path22.join(paths.wikiDir, "concepts", "index.md"),
14470
- path22.join(paths.wikiDir, "entities", "index.md"),
14471
- path22.join(paths.wikiDir, "outputs", "index.md"),
14472
- path22.join(paths.wikiDir, "projects", "index.md"),
14473
- path22.join(paths.wikiDir, "candidates", "index.md")
14692
+ path23.join(paths.wikiDir, "index.md"),
14693
+ path23.join(paths.wikiDir, "sources", "index.md"),
14694
+ path23.join(paths.wikiDir, "code", "index.md"),
14695
+ path23.join(paths.wikiDir, "concepts", "index.md"),
14696
+ path23.join(paths.wikiDir, "entities", "index.md"),
14697
+ path23.join(paths.wikiDir, "outputs", "index.md"),
14698
+ path23.join(paths.wikiDir, "projects", "index.md"),
14699
+ path23.join(paths.wikiDir, "candidates", "index.md")
14474
14700
  ];
14475
14701
  const checks = await Promise.all(requiredPaths.map((filePath) => fileExists(filePath)));
14476
14702
  return checks.every(Boolean);
14477
14703
  }
14478
14704
  async function loadAvailableCachedAnalyses(paths, manifests) {
14479
14705
  const analyses = await Promise.all(
14480
- manifests.map(async (manifest) => readJsonFile(path22.join(paths.analysesDir, `${manifest.sourceId}.json`)))
14706
+ manifests.map(async (manifest) => readJsonFile(path23.join(paths.analysesDir, `${manifest.sourceId}.json`)))
14481
14707
  );
14482
14708
  return analyses.filter((analysis) => Boolean(analysis));
14483
14709
  }
14484
14710
  function approvalManifestPath(paths, approvalId) {
14485
- return path22.join(paths.approvalsDir, approvalId, "manifest.json");
14711
+ return path23.join(paths.approvalsDir, approvalId, "manifest.json");
14486
14712
  }
14487
14713
  function approvalGraphPath(paths, approvalId) {
14488
- return path22.join(paths.approvalsDir, approvalId, "state", "graph.json");
14714
+ return path23.join(paths.approvalsDir, approvalId, "state", "graph.json");
14489
14715
  }
14490
14716
  async function readApprovalManifest(paths, approvalId) {
14491
14717
  const manifest = await readJsonFile(approvalManifestPath(paths, approvalId));
@@ -14495,7 +14721,7 @@ async function readApprovalManifest(paths, approvalId) {
14495
14721
  return manifest;
14496
14722
  }
14497
14723
  async function writeApprovalManifest(paths, manifest) {
14498
- await fs18.writeFile(approvalManifestPath(paths, manifest.approvalId), `${JSON.stringify(manifest, null, 2)}
14724
+ await fs19.writeFile(approvalManifestPath(paths, manifest.approvalId), `${JSON.stringify(manifest, null, 2)}
14499
14725
  `, "utf8");
14500
14726
  }
14501
14727
  async function buildApprovalEntries(paths, changedFiles, deletedPaths, previousGraph, graph, labelsByPath = /* @__PURE__ */ new Map()) {
@@ -14510,7 +14736,7 @@ async function buildApprovalEntries(paths, changedFiles, deletedPaths, previousG
14510
14736
  continue;
14511
14737
  }
14512
14738
  const previousPage = previousPagesById.get(nextPage.id);
14513
- const currentExists = await fileExists(path22.join(paths.wikiDir, file.relativePath));
14739
+ const currentExists = await fileExists(path23.join(paths.wikiDir, file.relativePath));
14514
14740
  if (previousPage && previousPage.path !== nextPage.path) {
14515
14741
  entries.push({
14516
14742
  pageId: nextPage.id,
@@ -14545,7 +14771,7 @@ async function buildApprovalEntries(paths, changedFiles, deletedPaths, previousG
14545
14771
  const previousPage = previousPagesByPath.get(deletedPath);
14546
14772
  entries.push({
14547
14773
  pageId: previousPage?.id ?? `page:${slugify(deletedPath)}`,
14548
- title: previousPage?.title ?? path22.basename(deletedPath, ".md"),
14774
+ title: previousPage?.title ?? path23.basename(deletedPath, ".md"),
14549
14775
  kind: previousPage?.kind ?? "index",
14550
14776
  changeType: "delete",
14551
14777
  status: "pending",
@@ -14558,16 +14784,16 @@ async function buildApprovalEntries(paths, changedFiles, deletedPaths, previousG
14558
14784
  }
14559
14785
  async function stageApprovalBundle(paths, changedFiles, deletedPaths, previousGraph, graph) {
14560
14786
  const approvalId = `compile-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}`;
14561
- const approvalDir = path22.join(paths.approvalsDir, approvalId);
14787
+ const approvalDir = path23.join(paths.approvalsDir, approvalId);
14562
14788
  await ensureDir(approvalDir);
14563
- await ensureDir(path22.join(approvalDir, "wiki"));
14564
- await ensureDir(path22.join(approvalDir, "state"));
14789
+ await ensureDir(path23.join(approvalDir, "wiki"));
14790
+ await ensureDir(path23.join(approvalDir, "state"));
14565
14791
  for (const file of changedFiles) {
14566
- const targetPath = path22.join(approvalDir, "wiki", file.relativePath);
14567
- await ensureDir(path22.dirname(targetPath));
14568
- await fs18.writeFile(targetPath, file.content, "utf8");
14792
+ const targetPath = path23.join(approvalDir, "wiki", file.relativePath);
14793
+ await ensureDir(path23.dirname(targetPath));
14794
+ await fs19.writeFile(targetPath, file.content, "utf8");
14569
14795
  }
14570
- await fs18.writeFile(path22.join(approvalDir, "state", "graph.json"), JSON.stringify(graph, null, 2), "utf8");
14796
+ await fs19.writeFile(path23.join(approvalDir, "state", "graph.json"), JSON.stringify(graph, null, 2), "utf8");
14571
14797
  await writeApprovalManifest(paths, {
14572
14798
  approvalId,
14573
14799
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -14633,13 +14859,13 @@ async function syncVaultArtifacts(rootDir, input) {
14633
14859
  confidence: 1
14634
14860
  });
14635
14861
  const sourceRecord = await buildManagedGraphPage(
14636
- path22.join(paths.wikiDir, preview.path),
14862
+ path23.join(paths.wikiDir, preview.path),
14637
14863
  {
14638
14864
  managedBy: "system",
14639
14865
  confidence: 1,
14640
14866
  compiledFrom: [manifest.sourceId]
14641
14867
  },
14642
- (metadata) => buildSourcePage(
14868
+ (metadata, existingContent) => buildSourcePage(
14643
14869
  manifest,
14644
14870
  analysis,
14645
14871
  sourceSchemaHash,
@@ -14650,7 +14876,8 @@ async function syncVaultArtifacts(rootDir, input) {
14650
14876
  projectIds: sourceProjectIds,
14651
14877
  extraTags: [...sourceCategoryTags, ...analysis.tags ?? []],
14652
14878
  sourceClass: manifest.sourceClass
14653
- }
14879
+ },
14880
+ existingContent
14654
14881
  )
14655
14882
  );
14656
14883
  records.push(sourceRecord);
@@ -14679,7 +14906,7 @@ async function syncVaultArtifacts(rootDir, input) {
14679
14906
  );
14680
14907
  records.push(
14681
14908
  await buildManagedGraphPage(
14682
- path22.join(paths.wikiDir, modulePreview.path),
14909
+ path23.join(paths.wikiDir, modulePreview.path),
14683
14910
  {
14684
14911
  managedBy: "system",
14685
14912
  confidence: 1,
@@ -14713,8 +14940,8 @@ async function syncVaultArtifacts(rootDir, input) {
14713
14940
  const relativePath = promoted ? activeAggregatePath(itemKind, slug) : candidatePagePathFor(itemKind, slug);
14714
14941
  const aggregateSourceClass2 = aggregateManifestSourceClass(input.manifests, sourceIds);
14715
14942
  const fallbackPaths = [
14716
- path22.join(paths.wikiDir, activeAggregatePath(itemKind, slug)),
14717
- path22.join(paths.wikiDir, candidatePagePathFor(itemKind, slug))
14943
+ path23.join(paths.wikiDir, activeAggregatePath(itemKind, slug)),
14944
+ path23.join(paths.wikiDir, candidatePagePathFor(itemKind, slug))
14718
14945
  ];
14719
14946
  const confidence = nodeConfidence(aggregate.sourceAnalyses.length);
14720
14947
  const preview = emptyGraphPage({
@@ -14732,7 +14959,7 @@ async function syncVaultArtifacts(rootDir, input) {
14732
14959
  status: promoted ? "active" : "candidate"
14733
14960
  });
14734
14961
  const pageRecord = await buildManagedGraphPage(
14735
- path22.join(paths.wikiDir, relativePath),
14962
+ path23.join(paths.wikiDir, relativePath),
14736
14963
  {
14737
14964
  status: promoted ? "active" : "candidate",
14738
14965
  managedBy: "system",
@@ -14740,7 +14967,7 @@ async function syncVaultArtifacts(rootDir, input) {
14740
14967
  compiledFrom: sourceIds,
14741
14968
  statePathCandidates: fallbackPaths
14742
14969
  },
14743
- (metadata) => buildAggregatePage(
14970
+ (metadata, existingContent) => buildAggregatePage(
14744
14971
  itemKind,
14745
14972
  aggregate.name,
14746
14973
  aggregate.descriptions,
@@ -14759,7 +14986,8 @@ async function syncVaultArtifacts(rootDir, input) {
14759
14986
  ...aggregate.sourceAnalyses.map((item) => item.summary)
14760
14987
  ]),
14761
14988
  sourceClass: aggregateSourceClass2
14762
- }
14989
+ },
14990
+ existingContent
14763
14991
  )
14764
14992
  );
14765
14993
  if (promoted && previousEntry?.status === "candidate") {
@@ -14813,6 +15041,7 @@ async function syncVaultArtifacts(rootDir, input) {
14813
15041
  );
14814
15042
  const preliminaryPages = [...basePages, ...graphOrientation.records.map((record) => record.page)];
14815
15043
  const dashboardRecords = await buildDashboardRecords(
15044
+ config,
14816
15045
  paths,
14817
15046
  {
14818
15047
  ...baseGraph,
@@ -14861,7 +15090,7 @@ async function syncVaultArtifacts(rootDir, input) {
14861
15090
  confidence: 1
14862
15091
  }),
14863
15092
  content: await buildManagedContent(
14864
- path22.join(paths.wikiDir, "projects", "index.md"),
15093
+ path23.join(paths.wikiDir, "projects", "index.md"),
14865
15094
  {
14866
15095
  managedBy: "system",
14867
15096
  compiledFrom: indexCompiledFrom(projectIndexRefs)
@@ -14885,7 +15114,7 @@ async function syncVaultArtifacts(rootDir, input) {
14885
15114
  records.push({
14886
15115
  page: projectIndexRef,
14887
15116
  content: await buildManagedContent(
14888
- path22.join(paths.wikiDir, projectIndexRef.path),
15117
+ path23.join(paths.wikiDir, projectIndexRef.path),
14889
15118
  {
14890
15119
  managedBy: "system",
14891
15120
  compiledFrom: indexCompiledFrom(Object.values(sections).flat())
@@ -14913,7 +15142,7 @@ async function syncVaultArtifacts(rootDir, input) {
14913
15142
  confidence: 1
14914
15143
  }),
14915
15144
  content: await buildManagedContent(
14916
- path22.join(paths.wikiDir, "index.md"),
15145
+ path23.join(paths.wikiDir, "index.md"),
14917
15146
  {
14918
15147
  managedBy: "system",
14919
15148
  compiledFrom: indexCompiledFrom(allPages)
@@ -14949,7 +15178,7 @@ async function syncVaultArtifacts(rootDir, input) {
14949
15178
  confidence: 1
14950
15179
  }),
14951
15180
  content: await buildManagedContent(
14952
- path22.join(paths.wikiDir, relativePath),
15181
+ path23.join(paths.wikiDir, relativePath),
14953
15182
  {
14954
15183
  managedBy: "system",
14955
15184
  compiledFrom: indexCompiledFrom(pages)
@@ -14960,12 +15189,12 @@ async function syncVaultArtifacts(rootDir, input) {
14960
15189
  }
14961
15190
  const nextPagePaths = new Set(records.map((record) => record.page.path));
14962
15191
  const obsoleteGraphPaths = (previousGraph?.pages ?? []).filter((page) => page.kind !== "output" && page.kind !== "insight").map((page) => page.path).filter((relativePath) => !nextPagePaths.has(relativePath));
14963
- const existingProjectIndexPaths = (await listFilesRecursive(paths.projectsDir)).filter((absolutePath) => absolutePath.endsWith(".md")).map((absolutePath) => toPosix(path22.relative(paths.wikiDir, absolutePath))).filter((relativePath) => !nextPagePaths.has(relativePath));
15192
+ const existingProjectIndexPaths = (await listFilesRecursive(paths.projectsDir)).filter((absolutePath) => absolutePath.endsWith(".md")).map((absolutePath) => toPosix(path23.relative(paths.wikiDir, absolutePath))).filter((relativePath) => !nextPagePaths.has(relativePath));
14964
15193
  const obsoletePaths = uniqueStrings3([...obsoleteGraphPaths, ...existingProjectIndexPaths]);
14965
15194
  const changedFiles = [];
14966
15195
  for (const record of records) {
14967
- const absolutePath = path22.join(paths.wikiDir, record.page.path);
14968
- const current = await fileExists(absolutePath) ? await fs18.readFile(absolutePath, "utf8") : null;
15196
+ const absolutePath = path23.join(paths.wikiDir, record.page.path);
15197
+ const current = await fileExists(absolutePath) ? await fs19.readFile(absolutePath, "utf8") : null;
14969
15198
  if (current !== record.content) {
14970
15199
  changedPages.push(record.page.path);
14971
15200
  changedFiles.push({ relativePath: record.page.path, content: record.content });
@@ -14990,10 +15219,10 @@ async function syncVaultArtifacts(rootDir, input) {
14990
15219
  await writePage(paths.wikiDir, record.page.path, record.content, writeChanges);
14991
15220
  }
14992
15221
  for (const relativePath of obsoletePaths) {
14993
- await fs18.rm(path22.join(paths.wikiDir, relativePath), { force: true });
15222
+ await fs19.rm(path23.join(paths.wikiDir, relativePath), { force: true });
14994
15223
  }
14995
15224
  await writeJsonFile(paths.graphPath, graph);
14996
- await writeJsonFile(path22.join(paths.wikiDir, "graph", "report.json"), graphOrientation.report);
15225
+ await writeJsonFile(path23.join(paths.wikiDir, "graph", "report.json"), graphOrientation.report);
14997
15226
  await writeJsonFile(paths.codeIndexPath, input.codeIndex);
14998
15227
  await writeJsonFile(paths.compileStatePath, {
14999
15228
  generatedAt: graph.generatedAt,
@@ -15052,6 +15281,7 @@ async function refreshIndexesAndSearch(rootDir, pages) {
15052
15281
  compileState?.generatedAt
15053
15282
  ) : { records: [], report: null };
15054
15283
  const dashboardRecords = currentGraph ? await buildDashboardRecords(
15284
+ config,
15055
15285
  paths,
15056
15286
  {
15057
15287
  ...currentGraph,
@@ -15088,18 +15318,18 @@ async function refreshIndexesAndSearch(rootDir, pages) {
15088
15318
  })
15089
15319
  );
15090
15320
  await Promise.all([
15091
- ensureDir(path22.join(paths.wikiDir, "sources")),
15092
- ensureDir(path22.join(paths.wikiDir, "code")),
15093
- ensureDir(path22.join(paths.wikiDir, "concepts")),
15094
- ensureDir(path22.join(paths.wikiDir, "entities")),
15095
- ensureDir(path22.join(paths.wikiDir, "outputs")),
15096
- ensureDir(path22.join(paths.wikiDir, "dashboards")),
15097
- ensureDir(path22.join(paths.wikiDir, "graph")),
15098
- ensureDir(path22.join(paths.wikiDir, "graph", "communities")),
15099
- ensureDir(path22.join(paths.wikiDir, "projects")),
15100
- ensureDir(path22.join(paths.wikiDir, "candidates"))
15321
+ ensureDir(path23.join(paths.wikiDir, "sources")),
15322
+ ensureDir(path23.join(paths.wikiDir, "code")),
15323
+ ensureDir(path23.join(paths.wikiDir, "concepts")),
15324
+ ensureDir(path23.join(paths.wikiDir, "entities")),
15325
+ ensureDir(path23.join(paths.wikiDir, "outputs")),
15326
+ ensureDir(path23.join(paths.wikiDir, "dashboards")),
15327
+ ensureDir(path23.join(paths.wikiDir, "graph")),
15328
+ ensureDir(path23.join(paths.wikiDir, "graph", "communities")),
15329
+ ensureDir(path23.join(paths.wikiDir, "projects")),
15330
+ ensureDir(path23.join(paths.wikiDir, "candidates"))
15101
15331
  ]);
15102
- const projectsIndexPath = path22.join(paths.wikiDir, "projects", "index.md");
15332
+ const projectsIndexPath = path23.join(paths.wikiDir, "projects", "index.md");
15103
15333
  await writeFileIfChanged(
15104
15334
  projectsIndexPath,
15105
15335
  await buildManagedContent(
@@ -15120,7 +15350,7 @@ async function refreshIndexesAndSearch(rootDir, pages) {
15120
15350
  outputs: pages.filter((page) => page.kind === "output" && page.projectIds.includes(project.id)),
15121
15351
  candidates: pages.filter((page) => page.status === "candidate" && page.projectIds.includes(project.id))
15122
15352
  };
15123
- const absolutePath = path22.join(paths.wikiDir, "projects", project.id, "index.md");
15353
+ const absolutePath = path23.join(paths.wikiDir, "projects", project.id, "index.md");
15124
15354
  await writeFileIfChanged(
15125
15355
  absolutePath,
15126
15356
  await buildManagedContent(
@@ -15138,7 +15368,7 @@ async function refreshIndexesAndSearch(rootDir, pages) {
15138
15368
  )
15139
15369
  );
15140
15370
  }
15141
- const rootIndexPath = path22.join(paths.wikiDir, "index.md");
15371
+ const rootIndexPath = path23.join(paths.wikiDir, "index.md");
15142
15372
  await writeFileIfChanged(
15143
15373
  rootIndexPath,
15144
15374
  await buildManagedContent(
@@ -15164,7 +15394,7 @@ async function refreshIndexesAndSearch(rootDir, pages) {
15164
15394
  ["candidates/index.md", "candidates", pagesWithGraph.filter((page) => page.status === "candidate")],
15165
15395
  ["graph/index.md", "graph", pagesWithGraph.filter((page) => page.kind === "graph_report" || page.kind === "community_summary")]
15166
15396
  ]) {
15167
- const absolutePath = path22.join(paths.wikiDir, relativePath);
15397
+ const absolutePath = path23.join(paths.wikiDir, relativePath);
15168
15398
  await writeFileIfChanged(
15169
15399
  absolutePath,
15170
15400
  await buildManagedContent(
@@ -15178,31 +15408,31 @@ async function refreshIndexesAndSearch(rootDir, pages) {
15178
15408
  );
15179
15409
  }
15180
15410
  for (const record of graphOrientation.records) {
15181
- await writeFileIfChanged(path22.join(paths.wikiDir, record.page.path), record.content);
15411
+ await writeFileIfChanged(path23.join(paths.wikiDir, record.page.path), record.content);
15182
15412
  }
15183
15413
  for (const record of dashboardRecords) {
15184
- await writeFileIfChanged(path22.join(paths.wikiDir, record.page.path), record.content);
15414
+ await writeFileIfChanged(path23.join(paths.wikiDir, record.page.path), record.content);
15185
15415
  }
15186
15416
  if (graphOrientation.report) {
15187
- await writeJsonFile(path22.join(paths.wikiDir, "graph", "report.json"), graphOrientation.report);
15417
+ await writeJsonFile(path23.join(paths.wikiDir, "graph", "report.json"), graphOrientation.report);
15188
15418
  }
15189
- const existingProjectIndexPaths = (await listFilesRecursive(paths.projectsDir)).filter((absolutePath) => absolutePath.endsWith(".md")).map((absolutePath) => toPosix(path22.relative(paths.wikiDir, absolutePath)));
15419
+ const existingProjectIndexPaths = (await listFilesRecursive(paths.projectsDir)).filter((absolutePath) => absolutePath.endsWith(".md")).map((absolutePath) => toPosix(path23.relative(paths.wikiDir, absolutePath)));
15190
15420
  const allowedProjectIndexPaths = /* @__PURE__ */ new Set([
15191
15421
  "projects/index.md",
15192
15422
  ...configuredProjects.map((project) => `projects/${project.id}/index.md`)
15193
15423
  ]);
15194
15424
  await Promise.all(
15195
- existingProjectIndexPaths.filter((relativePath) => !allowedProjectIndexPaths.has(relativePath)).map((relativePath) => fs18.rm(path22.join(paths.wikiDir, relativePath), { force: true }))
15425
+ existingProjectIndexPaths.filter((relativePath) => !allowedProjectIndexPaths.has(relativePath)).map((relativePath) => fs19.rm(path23.join(paths.wikiDir, relativePath), { force: true }))
15196
15426
  );
15197
- const existingGraphPages = (await listFilesRecursive(path22.join(paths.wikiDir, "graph").replace(/\/$/, "")).catch(() => [])).filter((absolutePath) => absolutePath.endsWith(".md")).map((absolutePath) => toPosix(path22.relative(paths.wikiDir, absolutePath)));
15427
+ const existingGraphPages = (await listFilesRecursive(path23.join(paths.wikiDir, "graph").replace(/\/$/, "")).catch(() => [])).filter((absolutePath) => absolutePath.endsWith(".md")).map((absolutePath) => toPosix(path23.relative(paths.wikiDir, absolutePath)));
15198
15428
  const allowedGraphPages = /* @__PURE__ */ new Set(["graph/index.md", ...graphOrientation.records.map((record) => record.page.path)]);
15199
15429
  await Promise.all(
15200
- existingGraphPages.filter((relativePath) => !allowedGraphPages.has(relativePath)).map((relativePath) => fs18.rm(path22.join(paths.wikiDir, relativePath), { force: true }))
15430
+ existingGraphPages.filter((relativePath) => !allowedGraphPages.has(relativePath)).map((relativePath) => fs19.rm(path23.join(paths.wikiDir, relativePath), { force: true }))
15201
15431
  );
15202
- const existingDashboardPages = (await listFilesRecursive(path22.join(paths.wikiDir, "dashboards")).catch(() => [])).filter((absolutePath) => absolutePath.endsWith(".md")).map((absolutePath) => toPosix(path22.relative(paths.wikiDir, absolutePath)));
15432
+ const existingDashboardPages = (await listFilesRecursive(path23.join(paths.wikiDir, "dashboards")).catch(() => [])).filter((absolutePath) => absolutePath.endsWith(".md")).map((absolutePath) => toPosix(path23.relative(paths.wikiDir, absolutePath)));
15203
15433
  const allowedDashboardPages = /* @__PURE__ */ new Set(["dashboards/index.md", ...dashboardRecords.map((record) => record.page.path)]);
15204
15434
  await Promise.all(
15205
- existingDashboardPages.filter((relativePath) => !allowedDashboardPages.has(relativePath)).map((relativePath) => fs18.rm(path22.join(paths.wikiDir, relativePath), { force: true }))
15435
+ existingDashboardPages.filter((relativePath) => !allowedDashboardPages.has(relativePath)).map((relativePath) => fs19.rm(path23.join(paths.wikiDir, relativePath), { force: true }))
15206
15436
  );
15207
15437
  await rebuildSearchIndex(paths.searchDbPath, pagesWithGraph, paths.wikiDir);
15208
15438
  }
@@ -15222,7 +15452,7 @@ async function prepareOutputPageSave(rootDir, input) {
15222
15452
  confidence: 0.74
15223
15453
  }
15224
15454
  });
15225
- const absolutePath = path22.join(paths.wikiDir, output.page.path);
15455
+ const absolutePath = path23.join(paths.wikiDir, output.page.path);
15226
15456
  return {
15227
15457
  page: output.page,
15228
15458
  savedPath: absolutePath,
@@ -15234,15 +15464,15 @@ async function prepareOutputPageSave(rootDir, input) {
15234
15464
  async function persistOutputPage(rootDir, input) {
15235
15465
  const { paths } = await loadVaultConfig(rootDir);
15236
15466
  const prepared = await prepareOutputPageSave(rootDir, input);
15237
- await ensureDir(path22.dirname(prepared.savedPath));
15238
- await fs18.writeFile(prepared.savedPath, prepared.content, "utf8");
15467
+ await ensureDir(path23.dirname(prepared.savedPath));
15468
+ await fs19.writeFile(prepared.savedPath, prepared.content, "utf8");
15239
15469
  for (const assetFile of prepared.assetFiles) {
15240
- const assetPath = path22.join(paths.wikiDir, assetFile.relativePath);
15241
- await ensureDir(path22.dirname(assetPath));
15470
+ const assetPath = path23.join(paths.wikiDir, assetFile.relativePath);
15471
+ await ensureDir(path23.dirname(assetPath));
15242
15472
  if (typeof assetFile.content === "string") {
15243
- await fs18.writeFile(assetPath, assetFile.content, assetFile.encoding ?? "utf8");
15473
+ await fs19.writeFile(assetPath, assetFile.content, assetFile.encoding ?? "utf8");
15244
15474
  } else {
15245
- await fs18.writeFile(assetPath, assetFile.content);
15475
+ await fs19.writeFile(assetPath, assetFile.content);
15246
15476
  }
15247
15477
  }
15248
15478
  return { page: prepared.page, savedPath: prepared.savedPath, outputAssets: prepared.outputAssets };
@@ -15263,7 +15493,7 @@ async function prepareExploreHubSave(rootDir, input) {
15263
15493
  confidence: 0.76
15264
15494
  }
15265
15495
  });
15266
- const absolutePath = path22.join(paths.wikiDir, hub.page.path);
15496
+ const absolutePath = path23.join(paths.wikiDir, hub.page.path);
15267
15497
  return {
15268
15498
  page: hub.page,
15269
15499
  savedPath: absolutePath,
@@ -15275,15 +15505,15 @@ async function prepareExploreHubSave(rootDir, input) {
15275
15505
  async function persistExploreHub(rootDir, input) {
15276
15506
  const { paths } = await loadVaultConfig(rootDir);
15277
15507
  const prepared = await prepareExploreHubSave(rootDir, input);
15278
- await ensureDir(path22.dirname(prepared.savedPath));
15279
- await fs18.writeFile(prepared.savedPath, prepared.content, "utf8");
15508
+ await ensureDir(path23.dirname(prepared.savedPath));
15509
+ await fs19.writeFile(prepared.savedPath, prepared.content, "utf8");
15280
15510
  for (const assetFile of prepared.assetFiles) {
15281
- const assetPath = path22.join(paths.wikiDir, assetFile.relativePath);
15282
- await ensureDir(path22.dirname(assetPath));
15511
+ const assetPath = path23.join(paths.wikiDir, assetFile.relativePath);
15512
+ await ensureDir(path23.dirname(assetPath));
15283
15513
  if (typeof assetFile.content === "string") {
15284
- await fs18.writeFile(assetPath, assetFile.content, assetFile.encoding ?? "utf8");
15514
+ await fs19.writeFile(assetPath, assetFile.content, assetFile.encoding ?? "utf8");
15285
15515
  } else {
15286
- await fs18.writeFile(assetPath, assetFile.content);
15516
+ await fs19.writeFile(assetPath, assetFile.content);
15287
15517
  }
15288
15518
  }
15289
15519
  return { page: prepared.page, savedPath: prepared.savedPath, outputAssets: prepared.outputAssets };
@@ -15301,17 +15531,17 @@ async function stageOutputApprovalBundle(rootDir, stagedPages, options = {}) {
15301
15531
  ]);
15302
15532
  const labelsByPath = new Map(stagedPages.filter((item) => item.label).map((item) => [item.page.path, item.label]));
15303
15533
  const approvalId = `schedule-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}`;
15304
- const approvalDir = path22.join(paths.approvalsDir, approvalId);
15534
+ const approvalDir = path23.join(paths.approvalsDir, approvalId);
15305
15535
  await ensureDir(approvalDir);
15306
- await ensureDir(path22.join(approvalDir, "wiki"));
15307
- await ensureDir(path22.join(approvalDir, "state"));
15536
+ await ensureDir(path23.join(approvalDir, "wiki"));
15537
+ await ensureDir(path23.join(approvalDir, "state"));
15308
15538
  for (const file of changedFiles) {
15309
- const targetPath = path22.join(approvalDir, "wiki", file.relativePath);
15310
- await ensureDir(path22.dirname(targetPath));
15539
+ const targetPath = path23.join(approvalDir, "wiki", file.relativePath);
15540
+ await ensureDir(path23.dirname(targetPath));
15311
15541
  if ("binary" in file && file.binary) {
15312
- await fs18.writeFile(targetPath, Buffer.from(file.content, "base64"));
15542
+ await fs19.writeFile(targetPath, Buffer.from(file.content, "base64"));
15313
15543
  } else {
15314
- await fs18.writeFile(targetPath, file.content, "utf8");
15544
+ await fs19.writeFile(targetPath, file.content, "utf8");
15315
15545
  }
15316
15546
  }
15317
15547
  const nextPages = sortGraphPages([
@@ -15326,12 +15556,13 @@ async function stageOutputApprovalBundle(rootDir, stagedPages, options = {}) {
15326
15556
  sources: previousGraph?.sources ?? [],
15327
15557
  pages: nextPages
15328
15558
  };
15329
- await fs18.writeFile(path22.join(approvalDir, "state", "graph.json"), JSON.stringify(graph, null, 2), "utf8");
15559
+ await fs19.writeFile(path23.join(approvalDir, "state", "graph.json"), JSON.stringify(graph, null, 2), "utf8");
15330
15560
  await writeApprovalManifest(paths, {
15331
15561
  approvalId,
15332
15562
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
15333
15563
  bundleType: options.bundleType ?? "generated_output",
15334
15564
  title: options.title,
15565
+ sourceSessionId: options.sourceSessionId,
15335
15566
  entries: await buildApprovalEntries(
15336
15567
  paths,
15337
15568
  stagedPages.map((item) => ({ relativePath: item.page.path, content: item.content })),
@@ -15361,9 +15592,9 @@ async function executeQuery(rootDir, question, format) {
15361
15592
  const searchResults = searchPages(paths.searchDbPath, question, 5);
15362
15593
  const excerpts = await Promise.all(
15363
15594
  searchResults.map(async (result) => {
15364
- const absolutePath = path22.join(paths.wikiDir, result.path);
15595
+ const absolutePath = path23.join(paths.wikiDir, result.path);
15365
15596
  try {
15366
- const content = await fs18.readFile(absolutePath, "utf8");
15597
+ const content = await fs19.readFile(absolutePath, "utf8");
15367
15598
  const parsed = matter9(content);
15368
15599
  return `# ${result.title}
15369
15600
  ${truncate(normalizeWhitespace(parsed.content), 1200)}`;
@@ -15598,7 +15829,7 @@ function computeChangeSummary(current, staged, changeType) {
15598
15829
  async function listApprovals(rootDir) {
15599
15830
  const { paths } = await loadVaultConfig(rootDir);
15600
15831
  const manifests = await Promise.all(
15601
- (await fs18.readdir(paths.approvalsDir, { withFileTypes: true }).catch(() => [])).filter((entry) => entry.isDirectory()).map(async (entry) => {
15832
+ (await fs19.readdir(paths.approvalsDir, { withFileTypes: true }).catch(() => [])).filter((entry) => entry.isDirectory()).map(async (entry) => {
15602
15833
  try {
15603
15834
  return await readApprovalManifest(paths, entry.name);
15604
15835
  } catch {
@@ -15614,8 +15845,8 @@ async function readApproval(rootDir, approvalId, options) {
15614
15845
  const details = await Promise.all(
15615
15846
  manifest.entries.map(async (entry) => {
15616
15847
  const currentPath = entry.previousPath ?? entry.nextPath;
15617
- const currentContent = currentPath ? await fs18.readFile(path22.join(paths.wikiDir, currentPath), "utf8").catch(() => void 0) : void 0;
15618
- const stagedContent = entry.nextPath ? await fs18.readFile(path22.join(paths.approvalsDir, approvalId, "wiki", entry.nextPath), "utf8").catch(() => void 0) : void 0;
15848
+ const currentContent = currentPath ? await fs19.readFile(path23.join(paths.wikiDir, currentPath), "utf8").catch(() => void 0) : void 0;
15849
+ const stagedContent = entry.nextPath ? await fs19.readFile(path23.join(paths.approvalsDir, approvalId, "wiki", entry.nextPath), "utf8").catch(() => void 0) : void 0;
15619
15850
  const detail = {
15620
15851
  ...entry,
15621
15852
  currentContent,
@@ -15648,26 +15879,26 @@ async function acceptApproval(rootDir, approvalId, targets = []) {
15648
15879
  if (!entry.nextPath) {
15649
15880
  throw new Error(`Approval entry ${entry.pageId} is missing a staged path.`);
15650
15881
  }
15651
- const stagedAbsolutePath = path22.join(paths.approvalsDir, approvalId, "wiki", entry.nextPath);
15652
- const stagedContent = await fs18.readFile(stagedAbsolutePath, "utf8");
15653
- const targetAbsolutePath = path22.join(paths.wikiDir, entry.nextPath);
15654
- await ensureDir(path22.dirname(targetAbsolutePath));
15655
- await fs18.writeFile(targetAbsolutePath, stagedContent, "utf8");
15882
+ const stagedAbsolutePath = path23.join(paths.approvalsDir, approvalId, "wiki", entry.nextPath);
15883
+ const stagedContent = await fs19.readFile(stagedAbsolutePath, "utf8");
15884
+ const targetAbsolutePath = path23.join(paths.wikiDir, entry.nextPath);
15885
+ await ensureDir(path23.dirname(targetAbsolutePath));
15886
+ await fs19.writeFile(targetAbsolutePath, stagedContent, "utf8");
15656
15887
  if (entry.changeType === "promote" && entry.previousPath) {
15657
- await fs18.rm(path22.join(paths.wikiDir, entry.previousPath), { force: true });
15888
+ await fs19.rm(path23.join(paths.wikiDir, entry.previousPath), { force: true });
15658
15889
  }
15659
15890
  const nextPage = bundleGraph?.pages.find((page) => page.id === entry.pageId && page.path === entry.nextPath) ?? parseStoredPage(entry.nextPath, stagedContent);
15660
15891
  if (nextPage.kind === "output" && nextPage.outputAssets?.length) {
15661
- const outputAssetDir = path22.join(paths.wikiDir, "outputs", "assets", path22.basename(nextPage.path, ".md"));
15662
- await fs18.rm(outputAssetDir, { recursive: true, force: true });
15892
+ const outputAssetDir = path23.join(paths.wikiDir, "outputs", "assets", path23.basename(nextPage.path, ".md"));
15893
+ await fs19.rm(outputAssetDir, { recursive: true, force: true });
15663
15894
  for (const asset of nextPage.outputAssets) {
15664
- const stagedAssetPath = path22.join(paths.approvalsDir, approvalId, "wiki", asset.path);
15895
+ const stagedAssetPath = path23.join(paths.approvalsDir, approvalId, "wiki", asset.path);
15665
15896
  if (!await fileExists(stagedAssetPath)) {
15666
15897
  continue;
15667
15898
  }
15668
- const targetAssetPath = path22.join(paths.wikiDir, asset.path);
15669
- await ensureDir(path22.dirname(targetAssetPath));
15670
- await fs18.copyFile(stagedAssetPath, targetAssetPath);
15899
+ const targetAssetPath = path23.join(paths.wikiDir, asset.path);
15900
+ await ensureDir(path23.dirname(targetAssetPath));
15901
+ await fs19.copyFile(stagedAssetPath, targetAssetPath);
15671
15902
  }
15672
15903
  }
15673
15904
  nextPages = nextPages.filter(
@@ -15678,10 +15909,10 @@ async function acceptApproval(rootDir, approvalId, targets = []) {
15678
15909
  } else {
15679
15910
  const deletedPage = nextPages.find((page) => page.id === entry.pageId || page.path === entry.previousPath) ?? bundleGraph?.pages.find((page) => page.id === entry.pageId || page.path === entry.previousPath) ?? null;
15680
15911
  if (entry.previousPath) {
15681
- await fs18.rm(path22.join(paths.wikiDir, entry.previousPath), { force: true });
15912
+ await fs19.rm(path23.join(paths.wikiDir, entry.previousPath), { force: true });
15682
15913
  }
15683
15914
  if (deletedPage?.kind === "output") {
15684
- await fs18.rm(path22.join(paths.wikiDir, "outputs", "assets", path22.basename(deletedPage.path, ".md")), {
15915
+ await fs19.rm(path23.join(paths.wikiDir, "outputs", "assets", path23.basename(deletedPage.path, ".md")), {
15685
15916
  recursive: true,
15686
15917
  force: true
15687
15918
  });
@@ -15704,6 +15935,9 @@ async function acceptApproval(rootDir, approvalId, targets = []) {
15704
15935
  await writeJsonFile(paths.compileStatePath, compileState);
15705
15936
  await refreshIndexesAndSearch(rootDir, nextGraph.pages);
15706
15937
  await writeApprovalManifest(paths, manifest);
15938
+ if (manifest.sourceSessionId) {
15939
+ await updateGuidedSourceSessionStatus(rootDir, manifest.sourceSessionId, "accepted");
15940
+ }
15707
15941
  await recordSession(rootDir, {
15708
15942
  operation: "review",
15709
15943
  title: `Accepted review entries from ${approvalId}`,
@@ -15730,6 +15964,9 @@ async function rejectApproval(rootDir, approvalId, targets = []) {
15730
15964
  entry.status = "rejected";
15731
15965
  }
15732
15966
  await writeApprovalManifest(paths, manifest);
15967
+ if (manifest.sourceSessionId) {
15968
+ await updateGuidedSourceSessionStatus(rootDir, manifest.sourceSessionId, "rejected");
15969
+ }
15733
15970
  await recordSession(rootDir, {
15734
15971
  operation: "review",
15735
15972
  title: `Rejected review entries from ${approvalId}`,
@@ -15772,7 +16009,7 @@ async function promoteCandidate(rootDir, target) {
15772
16009
  const { paths } = await loadVaultConfig(rootDir);
15773
16010
  const graph = await readJsonFile(paths.graphPath);
15774
16011
  const candidate = resolveCandidateTarget(graph?.pages ?? [], target);
15775
- const raw = await fs18.readFile(path22.join(paths.wikiDir, candidate.path), "utf8");
16012
+ const raw = await fs19.readFile(path23.join(paths.wikiDir, candidate.path), "utf8");
15776
16013
  const parsed = matter9(raw);
15777
16014
  const nextUpdatedAt = (/* @__PURE__ */ new Date()).toISOString();
15778
16015
  const nextContent = matter9.stringify(parsed.content, {
@@ -15784,10 +16021,10 @@ async function promoteCandidate(rootDir, target) {
15784
16021
  )
15785
16022
  });
15786
16023
  const nextPath = candidateActivePath(candidate);
15787
- const nextAbsolutePath = path22.join(paths.wikiDir, nextPath);
15788
- await ensureDir(path22.dirname(nextAbsolutePath));
15789
- await fs18.writeFile(nextAbsolutePath, nextContent, "utf8");
15790
- await fs18.rm(path22.join(paths.wikiDir, candidate.path), { force: true });
16024
+ const nextAbsolutePath = path23.join(paths.wikiDir, nextPath);
16025
+ await ensureDir(path23.dirname(nextAbsolutePath));
16026
+ await fs19.writeFile(nextAbsolutePath, nextContent, "utf8");
16027
+ await fs19.rm(path23.join(paths.wikiDir, candidate.path), { force: true });
15791
16028
  const nextPage = parseStoredPage(nextPath, nextContent, { createdAt: candidate.createdAt, updatedAt: nextUpdatedAt });
15792
16029
  const nextPages = sortGraphPages(
15793
16030
  (graph?.pages ?? []).filter((page) => page.id !== candidate.id && page.path !== candidate.path).concat(nextPage)
@@ -15832,7 +16069,7 @@ async function archiveCandidate(rootDir, target) {
15832
16069
  const { paths } = await loadVaultConfig(rootDir);
15833
16070
  const graph = await readJsonFile(paths.graphPath);
15834
16071
  const candidate = resolveCandidateTarget(graph?.pages ?? [], target);
15835
- await fs18.rm(path22.join(paths.wikiDir, candidate.path), { force: true });
16072
+ await fs19.rm(path23.join(paths.wikiDir, candidate.path), { force: true });
15836
16073
  const nextPages = sortGraphPages((graph?.pages ?? []).filter((page) => page.id !== candidate.id && page.path !== candidate.path));
15837
16074
  const nextGraph = {
15838
16075
  generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -15871,18 +16108,18 @@ async function archiveCandidate(rootDir, target) {
15871
16108
  }
15872
16109
  async function ensureObsidianWorkspace(rootDir) {
15873
16110
  const { config } = await loadVaultConfig(rootDir);
15874
- const obsidianDir = path22.join(rootDir, ".obsidian");
16111
+ const obsidianDir = path23.join(rootDir, ".obsidian");
15875
16112
  const projectIds = projectEntries(config).map((project) => project.id);
15876
16113
  await ensureDir(obsidianDir);
15877
16114
  await Promise.all([
15878
- writeJsonFile(path22.join(obsidianDir, "app.json"), {
16115
+ writeJsonFile(path23.join(obsidianDir, "app.json"), {
15879
16116
  alwaysUpdateLinks: true,
15880
16117
  newFileLocation: "folder",
15881
16118
  newFileFolderPath: "wiki/insights",
15882
16119
  useMarkdownLinks: false,
15883
16120
  attachmentFolderPath: "raw/assets"
15884
16121
  }),
15885
- writeJsonFile(path22.join(obsidianDir, "core-plugins.json"), [
16122
+ writeJsonFile(path23.join(obsidianDir, "core-plugins.json"), [
15886
16123
  "file-explorer",
15887
16124
  "global-search",
15888
16125
  "switcher",
@@ -15892,7 +16129,7 @@ async function ensureObsidianWorkspace(rootDir) {
15892
16129
  "tag-pane",
15893
16130
  "page-preview"
15894
16131
  ]),
15895
- writeJsonFile(path22.join(obsidianDir, "graph.json"), {
16132
+ writeJsonFile(path23.join(obsidianDir, "graph.json"), {
15896
16133
  "collapse-filter": false,
15897
16134
  search: "",
15898
16135
  showTags: true,
@@ -15904,7 +16141,7 @@ async function ensureObsidianWorkspace(rootDir) {
15904
16141
  })),
15905
16142
  localJumps: false
15906
16143
  }),
15907
- writeJsonFile(path22.join(obsidianDir, "workspace.json"), {
16144
+ writeJsonFile(path23.join(obsidianDir, "workspace.json"), {
15908
16145
  active: "root",
15909
16146
  lastOpenFiles: ["wiki/index.md", "wiki/projects/index.md", "wiki/candidates/index.md", "wiki/insights/index.md"],
15910
16147
  left: {
@@ -15917,21 +16154,27 @@ async function ensureObsidianWorkspace(rootDir) {
15917
16154
  ]);
15918
16155
  }
15919
16156
  async function initVault(rootDir, options = {}) {
15920
- const profile = options.profile ?? "default";
15921
- const { paths } = await initWorkspace(rootDir, { profile });
16157
+ const requestedProfile = options.profile ?? "default";
16158
+ const { config, paths } = await initWorkspace(rootDir, { profile: requestedProfile });
16159
+ const profile = config.profile;
16160
+ const isResearchProfile = profile.presets.length > 0 || profile.guidedSessionMode === "canonical_review" || profile.dataviewBlocks;
15922
16161
  await installConfiguredAgents(rootDir);
15923
- const insightsIndexPath = path22.join(paths.wikiDir, "insights", "index.md");
16162
+ const insightsIndexPath = path23.join(paths.wikiDir, "insights", "index.md");
15924
16163
  const now = (/* @__PURE__ */ new Date()).toISOString();
15925
16164
  await writeFileIfChanged(
15926
16165
  insightsIndexPath,
15927
16166
  matter9.stringify(
15928
- (profile === "personal-research" ? [
16167
+ (isResearchProfile ? [
15929
16168
  "# Insights",
15930
16169
  "",
15931
16170
  "Human-authored research notes live here.",
15932
16171
  "",
15933
16172
  "- Use this folder for thesis notes, reading reflections, synthesis drafts, and decisions you want to keep explicitly human-authored.",
15934
- "- Guided ingest can propose updates elsewhere, but SwarmVault does not rewrite files inside `wiki/insights/` after initialization.",
16173
+ ...profile.guidedSessionMode === "canonical_review" ? [
16174
+ "- Guided sessions can stage approval-queued updates for canonical pages and fall back to `wiki/insights/` when a claim still needs judgment."
16175
+ ] : [
16176
+ "- Guided sessions fall back to `wiki/insights/` for exploratory synthesis until you decide what should become canonical."
16177
+ ],
15935
16178
  "- Treat these pages as the human judgment layer for your vault.",
15936
16179
  ""
15937
16180
  ] : [
@@ -15940,7 +16183,7 @@ async function initVault(rootDir, options = {}) {
15940
16183
  "Human-authored notes live here.",
15941
16184
  "",
15942
16185
  "- SwarmVault can read these pages during compile and query.",
15943
- "- SwarmVault does not rewrite files inside `wiki/insights/` after initialization.",
16186
+ "- SwarmVault can stage insight-page updates through guided sessions, but it never applies them without review.",
15944
16187
  ""
15945
16188
  ]).join("\n"),
15946
16189
  {
@@ -15966,7 +16209,7 @@ async function initVault(rootDir, options = {}) {
15966
16209
  )
15967
16210
  );
15968
16211
  await writeFileIfChanged(
15969
- path22.join(paths.wikiDir, "projects", "index.md"),
16212
+ path23.join(paths.wikiDir, "projects", "index.md"),
15970
16213
  matter9.stringify(["# Projects", "", "- Run `swarmvault compile` to build project rollups.", ""].join("\n"), {
15971
16214
  page_id: "projects:index",
15972
16215
  kind: "index",
@@ -15989,7 +16232,7 @@ async function initVault(rootDir, options = {}) {
15989
16232
  })
15990
16233
  );
15991
16234
  await writeFileIfChanged(
15992
- path22.join(paths.wikiDir, "candidates", "index.md"),
16235
+ path23.join(paths.wikiDir, "candidates", "index.md"),
15993
16236
  matter9.stringify(["# Candidates", "", "- Run `swarmvault compile` to stage candidate pages.", ""].join("\n"), {
15994
16237
  page_id: "candidates:index",
15995
16238
  kind: "index",
@@ -16014,15 +16257,21 @@ async function initVault(rootDir, options = {}) {
16014
16257
  if (options.obsidian) {
16015
16258
  await ensureObsidianWorkspace(rootDir);
16016
16259
  }
16017
- if (profile === "personal-research") {
16260
+ if (isResearchProfile) {
16018
16261
  await writeFileIfChanged(
16019
- path22.join(paths.wikiDir, "insights", "research-playbook.md"),
16262
+ path23.join(paths.wikiDir, "insights", "research-playbook.md"),
16020
16263
  matter9.stringify(
16021
16264
  [
16022
- "# Personal Research Playbook",
16265
+ `# ${requestedProfile === "personal-research" ? "Personal Research Playbook" : "Research Playbook"}`,
16023
16266
  "",
16024
16267
  "- Add one source at a time with `swarmvault ingest <input> --guide` or `swarmvault source add <input> --guide`.",
16025
- "- Review `wiki/outputs/source-briefs/`, `wiki/outputs/source-reviews/`, and `wiki/outputs/source-guides/` before accepting staged updates.",
16268
+ "- Resume a guided session with `swarmvault source session <source-id-or-session-id>` whenever you want to answer the session prompts directly.",
16269
+ "- Review `wiki/outputs/source-briefs/`, `wiki/outputs/source-reviews/`, `wiki/outputs/source-guides/`, and `wiki/outputs/source-sessions/` before accepting staged updates.",
16270
+ ...profile.guidedSessionMode === "canonical_review" ? ["- Use `swarmvault review show --diff` to inspect staged canonical page edits before accepting them."] : ["- Keep exploratory synthesis in `wiki/insights/` until you are ready to promote it into canonical pages."],
16271
+ ...profile.dataviewBlocks ? [
16272
+ "- Dataview-friendly fields are enabled in the dashboards, but every generated page should still read cleanly as plain markdown."
16273
+ ] : [],
16274
+ ...profile.presets.length ? [`- Active profile presets: ${profile.presets.map((preset) => `\`${preset}\``).join(", ")}.`] : [],
16026
16275
  "- Keep unresolved questions visible in `wiki/dashboards/open-questions.md`.",
16027
16276
  "- Use `swarmvault review list` and `swarmvault review show --diff` to decide what becomes canonical.",
16028
16277
  ""
@@ -16030,7 +16279,7 @@ async function initVault(rootDir, options = {}) {
16030
16279
  {
16031
16280
  page_id: "insights:research-playbook",
16032
16281
  kind: "insight",
16033
- title: "Personal Research Playbook",
16282
+ title: requestedProfile === "personal-research" ? "Personal Research Playbook" : "Research Playbook",
16034
16283
  tags: ["insight", "research", "playbook"],
16035
16284
  source_ids: [],
16036
16285
  project_ids: [],
@@ -16165,7 +16414,7 @@ async function compileVault(rootDir, options = {}) {
16165
16414
  ),
16166
16415
  Promise.all(
16167
16416
  clean.map(async (manifest) => {
16168
- const cached = await readJsonFile(path22.join(paths.analysesDir, `${manifest.sourceId}.json`));
16417
+ const cached = await readJsonFile(path23.join(paths.analysesDir, `${manifest.sourceId}.json`));
16169
16418
  if (cached) {
16170
16419
  analysisProgress.tick(manifest.title);
16171
16420
  return cached;
@@ -16193,22 +16442,22 @@ async function compileVault(rootDir, options = {}) {
16193
16442
  }
16194
16443
  const enriched = enrichResolvedCodeImports(manifest, analysis, codeIndex);
16195
16444
  if (analysisSignature(enriched) !== analysisSignature(analysis)) {
16196
- await writeJsonFile(path22.join(paths.analysesDir, `${analysis.sourceId}.json`), enriched);
16445
+ await writeJsonFile(path23.join(paths.analysesDir, `${analysis.sourceId}.json`), enriched);
16197
16446
  }
16198
16447
  return enriched;
16199
16448
  })
16200
16449
  );
16201
16450
  await Promise.all([
16202
- ensureDir(path22.join(paths.wikiDir, "sources")),
16203
- ensureDir(path22.join(paths.wikiDir, "code")),
16204
- ensureDir(path22.join(paths.wikiDir, "concepts")),
16205
- ensureDir(path22.join(paths.wikiDir, "entities")),
16206
- ensureDir(path22.join(paths.wikiDir, "outputs")),
16207
- ensureDir(path22.join(paths.wikiDir, "projects")),
16208
- ensureDir(path22.join(paths.wikiDir, "insights")),
16209
- ensureDir(path22.join(paths.wikiDir, "candidates")),
16210
- ensureDir(path22.join(paths.wikiDir, "candidates", "concepts")),
16211
- ensureDir(path22.join(paths.wikiDir, "candidates", "entities"))
16451
+ ensureDir(path23.join(paths.wikiDir, "sources")),
16452
+ ensureDir(path23.join(paths.wikiDir, "code")),
16453
+ ensureDir(path23.join(paths.wikiDir, "concepts")),
16454
+ ensureDir(path23.join(paths.wikiDir, "entities")),
16455
+ ensureDir(path23.join(paths.wikiDir, "outputs")),
16456
+ ensureDir(path23.join(paths.wikiDir, "projects")),
16457
+ ensureDir(path23.join(paths.wikiDir, "insights")),
16458
+ ensureDir(path23.join(paths.wikiDir, "candidates")),
16459
+ ensureDir(path23.join(paths.wikiDir, "candidates", "concepts")),
16460
+ ensureDir(path23.join(paths.wikiDir, "candidates", "entities"))
16212
16461
  ]);
16213
16462
  const sync = await syncVaultArtifacts(rootDir, {
16214
16463
  schemas,
@@ -16355,7 +16604,7 @@ async function queryVault(rootDir, options) {
16355
16604
  assetFiles: staged.assetFiles
16356
16605
  }
16357
16606
  ]);
16358
- stagedPath = path22.join(approval.approvalDir, "wiki", staged.page.path);
16607
+ stagedPath = path23.join(approval.approvalDir, "wiki", staged.page.path);
16359
16608
  savedPageId = staged.page.id;
16360
16609
  approvalId = approval.approvalId;
16361
16610
  approvalDir = approval.approvalDir;
@@ -16611,9 +16860,9 @@ ${orchestrationNotes.join("\n")}
16611
16860
  approvalId = approval.approvalId;
16612
16861
  approvalDir = approval.approvalDir;
16613
16862
  stepResults.forEach((result, index) => {
16614
- result.stagedPath = path22.join(approval.approvalDir, "wiki", stagedStepPages[index]?.page.path ?? "");
16863
+ result.stagedPath = path23.join(approval.approvalDir, "wiki", stagedStepPages[index]?.page.path ?? "");
16615
16864
  });
16616
- stagedHubPath = path22.join(approval.approvalDir, "wiki", hubPage.path);
16865
+ stagedHubPath = path23.join(approval.approvalDir, "wiki", hubPage.path);
16617
16866
  } else {
16618
16867
  await refreshVaultAfterOutputSave(rootDir);
16619
16868
  }
@@ -16700,11 +16949,11 @@ async function benchmarkVault(rootDir, options = {}) {
16700
16949
  }
16701
16950
  }
16702
16951
  for (const page of graph.pages) {
16703
- const absolutePath = path22.join(paths.wikiDir, page.path);
16952
+ const absolutePath = path23.join(paths.wikiDir, page.path);
16704
16953
  if (!await fileExists(absolutePath)) {
16705
16954
  continue;
16706
16955
  }
16707
- const parsed = matter9(await fs18.readFile(absolutePath, "utf8"));
16956
+ const parsed = matter9(await fs19.readFile(absolutePath, "utf8"));
16708
16957
  pageContentsById.set(page.id, parsed.content);
16709
16958
  }
16710
16959
  const configuredQuestions = (config.benchmark?.questions ?? []).map((question) => normalizeWhitespace(question)).filter(Boolean);
@@ -16759,7 +17008,7 @@ async function listGraphHyperedges(rootDir, target, limit = 25) {
16759
17008
  }
16760
17009
  async function readGraphReport(rootDir) {
16761
17010
  const { paths } = await loadVaultConfig(rootDir);
16762
- return readJsonFile(path22.join(paths.wikiDir, "graph", "report.json"));
17011
+ return readJsonFile(path23.join(paths.wikiDir, "graph", "report.json"));
16763
17012
  }
16764
17013
  async function listGodNodes(rootDir, limit = 10) {
16765
17014
  const graph = await ensureCompiledGraph(rootDir);
@@ -16772,15 +17021,15 @@ async function listPages(rootDir) {
16772
17021
  }
16773
17022
  async function readPage(rootDir, relativePath) {
16774
17023
  const { paths } = await loadVaultConfig(rootDir);
16775
- const absolutePath = path22.resolve(paths.wikiDir, relativePath);
17024
+ const absolutePath = path23.resolve(paths.wikiDir, relativePath);
16776
17025
  if (!absolutePath.startsWith(paths.wikiDir) || !await fileExists(absolutePath)) {
16777
17026
  return null;
16778
17027
  }
16779
- const raw = await fs18.readFile(absolutePath, "utf8");
17028
+ const raw = await fs19.readFile(absolutePath, "utf8");
16780
17029
  const parsed = matter9(raw);
16781
17030
  return {
16782
17031
  path: relativePath,
16783
- title: typeof parsed.data.title === "string" ? parsed.data.title : path22.basename(relativePath, path22.extname(relativePath)),
17032
+ title: typeof parsed.data.title === "string" ? parsed.data.title : path23.basename(relativePath, path23.extname(relativePath)),
16784
17033
  frontmatter: parsed.data,
16785
17034
  content: parsed.content
16786
17035
  };
@@ -16816,7 +17065,7 @@ function structuralLintFindings(_rootDir, paths, graph, schemas, manifests, sour
16816
17065
  severity: "warning",
16817
17066
  code: "stale_page",
16818
17067
  message: `Page ${page.title} is stale because the vault schema changed.`,
16819
- pagePath: path22.join(paths.wikiDir, page.path),
17068
+ pagePath: path23.join(paths.wikiDir, page.path),
16820
17069
  relatedPageIds: [page.id]
16821
17070
  });
16822
17071
  }
@@ -16829,7 +17078,7 @@ function structuralLintFindings(_rootDir, paths, graph, schemas, manifests, sour
16829
17078
  severity: "warning",
16830
17079
  code: "stale_page",
16831
17080
  message: `Page ${page.title} is stale because source ${sourceId} changed.`,
16832
- pagePath: path22.join(paths.wikiDir, page.path),
17081
+ pagePath: path23.join(paths.wikiDir, page.path),
16833
17082
  relatedSourceIds: [sourceId],
16834
17083
  relatedPageIds: [page.id]
16835
17084
  });
@@ -16840,13 +17089,13 @@ function structuralLintFindings(_rootDir, paths, graph, schemas, manifests, sour
16840
17089
  severity: "info",
16841
17090
  code: "orphan_page",
16842
17091
  message: `Page ${page.title} has no backlinks.`,
16843
- pagePath: path22.join(paths.wikiDir, page.path),
17092
+ pagePath: path23.join(paths.wikiDir, page.path),
16844
17093
  relatedPageIds: [page.id]
16845
17094
  });
16846
17095
  }
16847
- const absolutePath = path22.join(paths.wikiDir, page.path);
17096
+ const absolutePath = path23.join(paths.wikiDir, page.path);
16848
17097
  if (await fileExists(absolutePath)) {
16849
- const content = await fs18.readFile(absolutePath, "utf8");
17098
+ const content = await fs19.readFile(absolutePath, "utf8");
16850
17099
  if (content.includes("## Claims")) {
16851
17100
  const uncited = content.split("\n").filter((line) => line.startsWith("- ") && !line.includes("[source:"));
16852
17101
  if (uncited.length) {
@@ -16963,7 +17212,7 @@ async function bootstrapDemo(rootDir, input) {
16963
17212
  }
16964
17213
 
16965
17214
  // src/mcp.ts
16966
- var SERVER_VERSION = "0.5.0";
17215
+ var SERVER_VERSION = "0.6.1";
16967
17216
  async function createMcpServer(rootDir) {
16968
17217
  const server = new McpServer({
16969
17218
  name: "swarmvault",
@@ -17234,7 +17483,7 @@ async function createMcpServer(rootDir) {
17234
17483
  },
17235
17484
  async () => {
17236
17485
  const { paths } = await loadVaultConfig(rootDir);
17237
- const files = (await listFilesRecursive(paths.sessionsDir)).filter((filePath) => filePath.endsWith(".md")).map((filePath) => toPosix(path23.relative(paths.sessionsDir, filePath))).sort();
17486
+ const files = (await listFilesRecursive(paths.sessionsDir)).filter((filePath) => filePath.endsWith(".md")).map((filePath) => toPosix(path24.relative(paths.sessionsDir, filePath))).sort();
17238
17487
  return asTextResource("swarmvault://sessions", JSON.stringify(files, null, 2));
17239
17488
  }
17240
17489
  );
@@ -17267,8 +17516,8 @@ async function createMcpServer(rootDir) {
17267
17516
  return asTextResource(`swarmvault://pages/${encodedPath}`, `Page not found: ${relativePath}`);
17268
17517
  }
17269
17518
  const { paths } = await loadVaultConfig(rootDir);
17270
- const absolutePath = path23.resolve(paths.wikiDir, relativePath);
17271
- return asTextResource(`swarmvault://pages/${encodedPath}`, await fs19.readFile(absolutePath, "utf8"));
17519
+ const absolutePath = path24.resolve(paths.wikiDir, relativePath);
17520
+ return asTextResource(`swarmvault://pages/${encodedPath}`, await fs20.readFile(absolutePath, "utf8"));
17272
17521
  }
17273
17522
  );
17274
17523
  server.registerResource(
@@ -17276,11 +17525,11 @@ async function createMcpServer(rootDir) {
17276
17525
  new ResourceTemplate("swarmvault://sessions/{path}", {
17277
17526
  list: async () => {
17278
17527
  const { paths } = await loadVaultConfig(rootDir);
17279
- const files = (await listFilesRecursive(paths.sessionsDir)).filter((filePath) => filePath.endsWith(".md")).map((filePath) => toPosix(path23.relative(paths.sessionsDir, filePath))).sort();
17528
+ const files = (await listFilesRecursive(paths.sessionsDir)).filter((filePath) => filePath.endsWith(".md")).map((filePath) => toPosix(path24.relative(paths.sessionsDir, filePath))).sort();
17280
17529
  return {
17281
17530
  resources: files.map((relativePath) => ({
17282
17531
  uri: `swarmvault://sessions/${encodeURIComponent(relativePath)}`,
17283
- name: path23.basename(relativePath, ".md"),
17532
+ name: path24.basename(relativePath, ".md"),
17284
17533
  title: relativePath,
17285
17534
  description: "SwarmVault session artifact",
17286
17535
  mimeType: "text/markdown"
@@ -17297,11 +17546,11 @@ async function createMcpServer(rootDir) {
17297
17546
  const { paths } = await loadVaultConfig(rootDir);
17298
17547
  const encodedPath = typeof variables.path === "string" ? variables.path : "";
17299
17548
  const relativePath = decodeURIComponent(encodedPath);
17300
- const absolutePath = path23.resolve(paths.sessionsDir, relativePath);
17549
+ const absolutePath = path24.resolve(paths.sessionsDir, relativePath);
17301
17550
  if (!absolutePath.startsWith(paths.sessionsDir) || !await fileExists(absolutePath)) {
17302
17551
  return asTextResource(`swarmvault://sessions/${encodedPath}`, `Session not found: ${relativePath}`);
17303
17552
  }
17304
- return asTextResource(`swarmvault://sessions/${encodedPath}`, await fs19.readFile(absolutePath, "utf8"));
17553
+ return asTextResource(`swarmvault://sessions/${encodedPath}`, await fs20.readFile(absolutePath, "utf8"));
17305
17554
  }
17306
17555
  );
17307
17556
  return server;
@@ -17349,13 +17598,13 @@ function asTextResource(uri, text) {
17349
17598
  }
17350
17599
 
17351
17600
  // src/schedule.ts
17352
- import fs20 from "fs/promises";
17353
- import path24 from "path";
17601
+ import fs21 from "fs/promises";
17602
+ import path25 from "path";
17354
17603
  function scheduleStatePath(schedulesDir, jobId) {
17355
- return path24.join(schedulesDir, `${encodeURIComponent(jobId)}.json`);
17604
+ return path25.join(schedulesDir, `${encodeURIComponent(jobId)}.json`);
17356
17605
  }
17357
17606
  function scheduleLockPath(schedulesDir, jobId) {
17358
- return path24.join(schedulesDir, `${encodeURIComponent(jobId)}.lock`);
17607
+ return path25.join(schedulesDir, `${encodeURIComponent(jobId)}.lock`);
17359
17608
  }
17360
17609
  function parseEveryDuration(value) {
17361
17610
  const match = value.trim().match(/^(\d+)(m|h|d)$/i);
@@ -17458,13 +17707,13 @@ async function acquireJobLease(rootDir, jobId) {
17458
17707
  const { paths } = await loadVaultConfig(rootDir);
17459
17708
  const leasePath = scheduleLockPath(paths.schedulesDir, jobId);
17460
17709
  await ensureDir(paths.schedulesDir);
17461
- const handle = await fs20.open(leasePath, "wx");
17710
+ const handle = await fs21.open(leasePath, "wx");
17462
17711
  await handle.writeFile(`${process.pid}
17463
17712
  ${(/* @__PURE__ */ new Date()).toISOString()}
17464
17713
  `);
17465
17714
  await handle.close();
17466
17715
  return async () => {
17467
- await fs20.rm(leasePath, { force: true });
17716
+ await fs21.rm(leasePath, { force: true });
17468
17717
  };
17469
17718
  }
17470
17719
  async function listSchedules(rootDir) {
@@ -17612,8 +17861,9 @@ async function serveSchedules(rootDir, pollMs = 3e4) {
17612
17861
 
17613
17862
  // src/sources.ts
17614
17863
  import { spawn as spawn2 } from "child_process";
17615
- import fs21 from "fs/promises";
17616
- import path25 from "path";
17864
+ import fs22 from "fs/promises";
17865
+ import path26 from "path";
17866
+ import matter10 from "gray-matter";
17617
17867
  import { JSDOM as JSDOM3 } from "jsdom";
17618
17868
  var DEFAULT_CRAWL_MAX_PAGES = 12;
17619
17869
  var DEFAULT_CRAWL_MAX_DEPTH = 2;
@@ -17640,24 +17890,24 @@ function normalizeManagedStatus(value) {
17640
17890
  return value === "missing" || value === "error" ? value : "ready";
17641
17891
  }
17642
17892
  function withinRoot2(rootPath, targetPath) {
17643
- const relative = path25.relative(rootPath, targetPath);
17644
- return relative === "" || !relative.startsWith("..") && !path25.isAbsolute(relative);
17893
+ const relative = path26.relative(rootPath, targetPath);
17894
+ return relative === "" || !relative.startsWith("..") && !path26.isAbsolute(relative);
17645
17895
  }
17646
17896
  async function findNearestGitRoot3(startPath) {
17647
- let current = path25.resolve(startPath);
17897
+ let current = path26.resolve(startPath);
17648
17898
  try {
17649
- const stat = await fs21.stat(current);
17899
+ const stat = await fs22.stat(current);
17650
17900
  if (!stat.isDirectory()) {
17651
- current = path25.dirname(current);
17901
+ current = path26.dirname(current);
17652
17902
  }
17653
17903
  } catch {
17654
- current = path25.dirname(current);
17904
+ current = path26.dirname(current);
17655
17905
  }
17656
17906
  while (true) {
17657
- if (await fileExists(path25.join(current, ".git"))) {
17907
+ if (await fileExists(path26.join(current, ".git"))) {
17658
17908
  return current;
17659
17909
  }
17660
- const parent = path25.dirname(current);
17910
+ const parent = path26.dirname(current);
17661
17911
  if (parent === current) {
17662
17912
  return null;
17663
17913
  }
@@ -17731,7 +17981,7 @@ function isAllowedDocsCandidate(candidate, startUrl) {
17731
17981
  if (candidate.origin !== startUrl.origin) {
17732
17982
  return false;
17733
17983
  }
17734
- const extension = path25.extname(candidate.pathname).toLowerCase();
17984
+ const extension = path26.extname(candidate.pathname).toLowerCase();
17735
17985
  if (extension && extension !== ".html" && extension !== ".htm" && extension !== ".md") {
17736
17986
  return false;
17737
17987
  }
@@ -17820,14 +18070,14 @@ function matchesManagedSourceSpec(existing, input) {
17820
18070
  return false;
17821
18071
  }
17822
18072
  if (input.kind === "directory" || input.kind === "file") {
17823
- return path25.resolve(existing.path ?? "") === path25.resolve(input.path);
18073
+ return path26.resolve(existing.path ?? "") === path26.resolve(input.path);
17824
18074
  }
17825
18075
  return (existing.url ?? "") === input.url;
17826
18076
  }
17827
18077
  async function resolveManagedSourceInput(rootDir, input) {
17828
- const absoluteInput = path25.resolve(rootDir, input);
18078
+ const absoluteInput = path26.resolve(rootDir, input);
17829
18079
  if (!(input.startsWith("http://") || input.startsWith("https://"))) {
17830
- const stat = await fs21.stat(absoluteInput).catch(() => null);
18080
+ const stat = await fs22.stat(absoluteInput).catch(() => null);
17831
18081
  if (!stat) {
17832
18082
  throw new Error(`Source not found: ${input}`);
17833
18083
  }
@@ -17835,7 +18085,7 @@ async function resolveManagedSourceInput(rootDir, input) {
17835
18085
  return {
17836
18086
  kind: "file",
17837
18087
  path: absoluteInput,
17838
- title: path25.basename(absoluteInput, path25.extname(absoluteInput)) || absoluteInput
18088
+ title: path26.basename(absoluteInput, path26.extname(absoluteInput)) || absoluteInput
17839
18089
  };
17840
18090
  }
17841
18091
  if (!stat.isDirectory()) {
@@ -17847,7 +18097,7 @@ async function resolveManagedSourceInput(rootDir, input) {
17847
18097
  kind: "directory",
17848
18098
  path: absoluteInput,
17849
18099
  repoRoot,
17850
- title: path25.basename(absoluteInput) || absoluteInput
18100
+ title: path26.basename(absoluteInput) || absoluteInput
17851
18101
  };
17852
18102
  }
17853
18103
  const github = normalizeGitHubRepoRootUrl(input);
@@ -17870,16 +18120,16 @@ async function resolveManagedSourceInput(rootDir, input) {
17870
18120
  };
17871
18121
  }
17872
18122
  function directorySourceIdsFor(manifests, inputPath) {
17873
- return manifests.filter((manifest) => manifest.originalPath && withinRoot2(path25.resolve(inputPath), path25.resolve(manifest.originalPath))).map((manifest) => manifest.sourceId).sort((left, right) => left.localeCompare(right));
18123
+ return manifests.filter((manifest) => manifest.originalPath && withinRoot2(path26.resolve(inputPath), path26.resolve(manifest.originalPath))).map((manifest) => manifest.sourceId).sort((left, right) => left.localeCompare(right));
17874
18124
  }
17875
18125
  function fileSourceIdsFor(manifests, inputPath) {
17876
- const absoluteInput = path25.resolve(inputPath);
17877
- return manifests.filter((manifest) => manifest.originalPath && path25.resolve(manifest.originalPath) === absoluteInput).map((manifest) => manifest.sourceId).sort((left, right) => left.localeCompare(right));
18126
+ const absoluteInput = path26.resolve(inputPath);
18127
+ return manifests.filter((manifest) => manifest.originalPath && path26.resolve(manifest.originalPath) === absoluteInput).map((manifest) => manifest.sourceId).sort((left, right) => left.localeCompare(right));
17878
18128
  }
17879
18129
  async function syncDirectorySource(rootDir, inputPath, repoRoot) {
17880
18130
  const manifestsBefore = await listManifests(rootDir);
17881
18131
  const previousInScope = manifestsBefore.filter(
17882
- (manifest) => manifest.originalPath && withinRoot2(path25.resolve(inputPath), path25.resolve(manifest.originalPath))
18132
+ (manifest) => manifest.originalPath && withinRoot2(path26.resolve(inputPath), path26.resolve(manifest.originalPath))
17883
18133
  );
17884
18134
  const result = await ingestDirectory(rootDir, inputPath, { repoRoot });
17885
18135
  const removed = [];
@@ -17887,7 +18137,7 @@ async function syncDirectorySource(rootDir, inputPath, repoRoot) {
17887
18137
  if (!manifest.originalPath) {
17888
18138
  continue;
17889
18139
  }
17890
- if (await fileExists(path25.resolve(manifest.originalPath))) {
18140
+ if (await fileExists(path26.resolve(manifest.originalPath))) {
17891
18141
  continue;
17892
18142
  }
17893
18143
  const removedManifest = await removeManifestBySourceId(rootDir, manifest.sourceId);
@@ -17897,7 +18147,7 @@ async function syncDirectorySource(rootDir, inputPath, repoRoot) {
17897
18147
  }
17898
18148
  const manifestsAfter = await listManifests(rootDir);
17899
18149
  return {
17900
- title: path25.basename(inputPath) || inputPath,
18150
+ title: path26.basename(inputPath) || inputPath,
17901
18151
  sourceIds: directorySourceIdsFor(manifestsAfter, inputPath),
17902
18152
  counts: {
17903
18153
  scannedCount: result.scannedCount,
@@ -17913,7 +18163,7 @@ async function syncFileSource(rootDir, inputPath) {
17913
18163
  const result = await ingestInputDetailed(rootDir, inputPath);
17914
18164
  const manifestsAfter = await listManifests(rootDir);
17915
18165
  return {
17916
- title: path25.basename(inputPath, path25.extname(inputPath)) || inputPath,
18166
+ title: path26.basename(inputPath, path26.extname(inputPath)) || inputPath,
17917
18167
  sourceIds: fileSourceIdsFor(manifestsAfter, inputPath),
17918
18168
  counts: {
17919
18169
  scannedCount: result.scannedCount,
@@ -17947,8 +18197,8 @@ async function runGitCommand(cwd, args) {
17947
18197
  }
17948
18198
  async function syncGitHubRepoSource(rootDir, entry) {
17949
18199
  const workingDir = await managedSourceWorkingDir(rootDir, entry.id);
17950
- const checkoutDir = path25.join(workingDir, "checkout");
17951
- await fs21.rm(checkoutDir, { recursive: true, force: true });
18200
+ const checkoutDir = path26.join(workingDir, "checkout");
18201
+ await fs22.rm(checkoutDir, { recursive: true, force: true });
17952
18202
  await ensureDir(workingDir);
17953
18203
  if (!entry.url) {
17954
18204
  throw new Error(`Managed source ${entry.id} is missing its repository URL.`);
@@ -18075,7 +18325,7 @@ function scopedNodeIds(graph, sourceIds) {
18075
18325
  async function loadSourceAnalyses(rootDir, sourceIds) {
18076
18326
  const { paths } = await loadVaultConfig(rootDir);
18077
18327
  const analyses = await Promise.all(
18078
- sourceIds.map(async (sourceId) => await readJsonFile(path25.join(paths.analysesDir, `${sourceId}.json`)))
18328
+ sourceIds.map(async (sourceId) => await readJsonFile(path26.join(paths.analysesDir, `${sourceId}.json`)))
18079
18329
  );
18080
18330
  return analyses.filter((analysis) => Boolean(analysis?.sourceId));
18081
18331
  }
@@ -18235,9 +18485,9 @@ async function writeSourceBriefForScope(rootDir, source) {
18235
18485
  confidence: 0.82
18236
18486
  }
18237
18487
  });
18238
- const absolutePath = path25.join(paths.wikiDir, output.page.path);
18239
- await ensureDir(path25.dirname(absolutePath));
18240
- await fs21.writeFile(absolutePath, output.content, "utf8");
18488
+ const absolutePath = path26.join(paths.wikiDir, output.page.path);
18489
+ await ensureDir(path26.dirname(absolutePath));
18490
+ await fs22.writeFile(absolutePath, output.content, "utf8");
18241
18491
  return absolutePath;
18242
18492
  }
18243
18493
  async function writeSourceBrief(rootDir, source) {
@@ -18256,6 +18506,96 @@ async function generateBriefsForSources(rootDir, sources) {
18256
18506
  }
18257
18507
  return briefPaths;
18258
18508
  }
18509
+ var GUIDED_SESSION_QUESTIONS = [
18510
+ {
18511
+ id: "importance",
18512
+ prompt: "What matters most from this source for your wiki right now?"
18513
+ },
18514
+ {
18515
+ id: "exclude",
18516
+ prompt: "What should stay provisional, be ignored, or be kept out for now?"
18517
+ },
18518
+ {
18519
+ id: "targets",
18520
+ prompt: "Which canonical pages or topics should this source update?"
18521
+ },
18522
+ {
18523
+ id: "conflicts",
18524
+ prompt: "What feels new, reinforcing, or conflicting compared with what you already believe?"
18525
+ },
18526
+ {
18527
+ id: "followups",
18528
+ prompt: "What follow-up questions or next sources should stay open?"
18529
+ }
18530
+ ];
18531
+ function defaultGuidedSessionQuestions() {
18532
+ return GUIDED_SESSION_QUESTIONS.map((question) => ({ ...question }));
18533
+ }
18534
+ function splitDelimitedDetail(value) {
18535
+ return value ? value.split(",").map((item) => item.trim()).filter(Boolean) : [];
18536
+ }
18537
+ function normalizeGuidedAnswerValue(value) {
18538
+ return typeof value === "string" && value.trim() ? value.trim() : void 0;
18539
+ }
18540
+ function normalizeGuidedAnswers(input) {
18541
+ if (!input) {
18542
+ return {};
18543
+ }
18544
+ if (Array.isArray(input)) {
18545
+ return Object.fromEntries(
18546
+ GUIDED_SESSION_QUESTIONS.map((question, index) => [question.id, normalizeGuidedAnswerValue(input[index])]).filter(
18547
+ (entry) => Boolean(entry[1])
18548
+ )
18549
+ );
18550
+ }
18551
+ return Object.fromEntries(
18552
+ Object.entries(input).map(([key, value]) => [key, normalizeGuidedAnswerValue(value)]).filter((entry) => Boolean(entry[1]))
18553
+ );
18554
+ }
18555
+ function mergeGuidedSessionQuestions(questions, answers) {
18556
+ const normalizedAnswers = normalizeGuidedAnswers(answers);
18557
+ return questions.map((question) => ({
18558
+ ...question,
18559
+ answer: normalizedAnswers[question.id] ?? question.answer
18560
+ }));
18561
+ }
18562
+ function answeredGuidedSessionQuestions(questions) {
18563
+ return questions.filter((question) => typeof question.answer === "string" && question.answer.trim().length > 0);
18564
+ }
18565
+ function questionStateForSession(session) {
18566
+ return answeredGuidedSessionQuestions(session.questions).length === session.questions.length ? "answered" : "awaiting_input";
18567
+ }
18568
+ function manifestsForScope(graph, scope) {
18569
+ if (!graph) {
18570
+ return [];
18571
+ }
18572
+ const scopeSet = new Set(scope.sourceIds);
18573
+ return graph.sources.filter((manifest) => scopeSet.has(manifest.sourceId));
18574
+ }
18575
+ function scopeSourceType(scope, manifests) {
18576
+ return scope.kind ?? manifests[0]?.sourceKind ?? manifests[0]?.sourceType;
18577
+ }
18578
+ function scopeOccurredAt(manifests) {
18579
+ return manifests.map((manifest) => manifest.details?.occurred_at).filter((value) => typeof value === "string" && value.trim().length > 0).sort((left, right) => right.localeCompare(left))[0];
18580
+ }
18581
+ function scopeParticipants(manifests) {
18582
+ return uniqueStrings4(manifests.flatMap((manifest) => splitDelimitedDetail(manifest.details?.participants)));
18583
+ }
18584
+ function scopeContainerTitle(manifests) {
18585
+ return manifests.find((manifest) => manifest.details?.container_title)?.details?.container_title ?? manifests[0]?.sourceGroupTitle;
18586
+ }
18587
+ function scopeConversationId(manifests) {
18588
+ return manifests.find((manifest) => manifest.details?.conversation_id)?.details?.conversation_id;
18589
+ }
18590
+ function classifyGuidedEvidenceState(scope, targetPage, contradictions) {
18591
+ if (contradictions.length) {
18592
+ return "conflicting";
18593
+ }
18594
+ if (!targetPage) {
18595
+ return "needs_judgment";
18596
+ }
18597
+ return targetPage.sourceIds.some((sourceId) => !scope.sourceIds.includes(sourceId)) ? "reinforcing" : "new";
18598
+ }
18259
18599
  function renderDeterministicSourceReview(input) {
18260
18600
  const canonicalPages = input.sourcePages.filter((page) => page.kind === "source" || page.kind === "concept" || page.kind === "entity").slice(0, 10);
18261
18601
  const modulePages = input.sourcePages.filter((page) => page.kind === "module").slice(0, 8);
@@ -18356,12 +18696,13 @@ Entities: ${analysis.entities.map((entity) => entity.name).join(", ") || "none"}
18356
18696
  }
18357
18697
  }
18358
18698
  async function buildSourceReviewStagedPage(rootDir, scope) {
18359
- const { paths } = await loadVaultConfig(rootDir);
18699
+ const { config, paths } = await loadVaultConfig(rootDir);
18360
18700
  const markdown = await generateSourceReviewMarkdown(rootDir, scope);
18361
18701
  if (!markdown) {
18362
18702
  throw new Error(`Could not generate a source review for ${scope.id}.`);
18363
18703
  }
18364
18704
  const graph = await readJsonFile(paths.graphPath);
18705
+ const scopeManifests = manifestsForScope(graph, scope);
18365
18706
  const relatedPages = graph ? scopedSourcePages(graph, scope.sourceIds) : [];
18366
18707
  const relatedPageIds = relatedPages.slice(0, 16).map((page) => page.id);
18367
18708
  const relatedNodeIds = graph ? scopedNodeIds(graph, scope.sourceIds).slice(0, 24) : [];
@@ -18388,17 +18729,99 @@ async function buildSourceReviewStagedPage(rootDir, scope) {
18388
18729
  compiledFrom: scope.sourceIds,
18389
18730
  managedBy: "system",
18390
18731
  confidence: 0.79
18732
+ },
18733
+ frontmatter: {
18734
+ profile_presets: config.profile.presets,
18735
+ source_type: scopeSourceType(scope, scopeManifests),
18736
+ occurred_at: scopeOccurredAt(scopeManifests),
18737
+ participants: scopeParticipants(scopeManifests),
18738
+ container_title: scopeContainerTitle(scopeManifests),
18739
+ conversation_id: scopeConversationId(scopeManifests),
18740
+ question_state: "answered",
18741
+ canonical_targets: relatedPages.filter((page) => page.kind === "source" || page.kind === "concept" || page.kind === "entity").slice(0, 8).map((page) => page.path),
18742
+ evidence_state: findContradictionsForScope(scope, await readGraphReport(rootDir)).length ? "conflicting" : "needs_judgment"
18391
18743
  }
18392
18744
  });
18393
18745
  return { page: output.page, content: output.content };
18394
18746
  }
18395
18747
  function classifySourceGuidePageBuckets(sourcePages, scopeSourceIds) {
18396
18748
  const scopeSet = new Set(scopeSourceIds);
18397
- const canonicalPages = sourcePages.filter((page) => page.kind === "source" || page.kind === "concept" || page.kind === "entity").slice(0, 12);
18749
+ const canonicalPages = sourcePages.filter(
18750
+ (page) => (page.kind === "source" || page.kind === "concept" || page.kind === "entity") && (page.kind === "source" || page.status !== "candidate")
18751
+ ).slice(0, 12);
18398
18752
  const newPages = canonicalPages.filter((page) => page.sourceIds.every((sourceId) => scopeSet.has(sourceId))).slice(0, 6);
18399
18753
  const reinforcingPages = canonicalPages.filter((page) => page.sourceIds.some((sourceId) => !scopeSet.has(sourceId))).slice(0, 6);
18400
18754
  return { canonicalPages, newPages, reinforcingPages };
18401
18755
  }
18756
+ function findContradictionsForScope(scope, report) {
18757
+ return report?.contradictions.filter(
18758
+ (contradiction) => scope.sourceIds.includes(contradiction.sourceIdA) || scope.sourceIds.includes(contradiction.sourceIdB)
18759
+ ) ?? [];
18760
+ }
18761
+ function selectGuidedTargetPages(scope, sourcePages, questions) {
18762
+ const { canonicalPages } = classifySourceGuidePageBuckets(sourcePages, scope.sourceIds);
18763
+ if (!canonicalPages.length) {
18764
+ return [];
18765
+ }
18766
+ const desiredTargets = normalizeWhitespace(
18767
+ questions.find((question) => question.id === "targets")?.answer ?? questions.find((question) => question.id === "importance")?.answer ?? ""
18768
+ ).toLowerCase();
18769
+ const matchedTargets = desiredTargets ? canonicalPages.filter((page) => {
18770
+ const title = page.title.toLowerCase();
18771
+ const relative = page.path.replace(/\.md$/, "").toLowerCase();
18772
+ return desiredTargets.includes(title) || desiredTargets.includes(relative) || title.includes(desiredTargets);
18773
+ }) : [];
18774
+ return (matchedTargets.length ? matchedTargets : canonicalPages).slice(0, 6);
18775
+ }
18776
+ function insightRelativePathForTarget(page, scope) {
18777
+ const basename = path26.basename(page.path);
18778
+ if (page.kind === "concept") {
18779
+ return `insights/concepts/${basename}`;
18780
+ }
18781
+ if (page.kind === "entity") {
18782
+ return `insights/entities/${basename}`;
18783
+ }
18784
+ if (page.kind === "source") {
18785
+ return `insights/sources/${slugify(page.title || scope.title)}.md`;
18786
+ }
18787
+ return `insights/topics/${slugify(page.title || scope.title)}.md`;
18788
+ }
18789
+ function insightTitleForTarget(page, scope) {
18790
+ if (page.kind === "concept" || page.kind === "entity") {
18791
+ return page.title;
18792
+ }
18793
+ if (page.kind === "source") {
18794
+ return `Source Notes: ${page.title}`;
18795
+ }
18796
+ return `${scope.title} Notes`;
18797
+ }
18798
+ function insightTagsForTarget(page) {
18799
+ return uniqueStrings4(["insight", "guided-session", `guided/${page?.kind ?? "topic"}`]);
18800
+ }
18801
+ function guidedUpdateMarker(scopeId) {
18802
+ return {
18803
+ start: `<!-- swarmvault-guided-source:${scopeId}:start -->`,
18804
+ end: `<!-- swarmvault-guided-source:${scopeId}:end -->`
18805
+ };
18806
+ }
18807
+ function replaceMarkedSection(content, scopeId, replacement) {
18808
+ const marker = guidedUpdateMarker(scopeId);
18809
+ const block = `${marker.start}
18810
+ ${replacement.trim()}
18811
+ ${marker.end}`;
18812
+ const startIndex = content.indexOf(marker.start);
18813
+ const endIndex = content.indexOf(marker.end);
18814
+ if (startIndex !== -1 && endIndex !== -1 && endIndex > startIndex) {
18815
+ return `${content.slice(0, startIndex).trimEnd()}
18816
+
18817
+ ${block}
18818
+ `;
18819
+ }
18820
+ return `${content.trimEnd()}
18821
+
18822
+ ${block}
18823
+ `;
18824
+ }
18402
18825
  function renderDeterministicSourceGuide(input) {
18403
18826
  const { canonicalPages, newPages, reinforcingPages } = classifySourceGuidePageBuckets(input.sourcePages, input.scope.sourceIds);
18404
18827
  const modulePages = input.sourcePages.filter((page) => page.kind === "module").slice(0, 6);
@@ -18518,13 +18941,16 @@ Entities: ${analysis.entities.map((entity) => entity.name).join(", ") || "none"}
18518
18941
  }
18519
18942
  }
18520
18943
  async function buildSourceGuideStagedPage(rootDir, scope) {
18521
- const { paths } = await loadVaultConfig(rootDir);
18944
+ const { config, paths } = await loadVaultConfig(rootDir);
18522
18945
  const markdown = await generateSourceGuideMarkdown(rootDir, scope);
18523
18946
  if (!markdown) {
18524
18947
  throw new Error(`Could not generate a source guide for ${scope.id}.`);
18525
18948
  }
18526
18949
  const graph = await readJsonFile(paths.graphPath);
18950
+ const scopeManifests = manifestsForScope(graph, scope);
18527
18951
  const relatedPages = graph ? scopedSourcePages(graph, scope.sourceIds) : [];
18952
+ const contradictions = findContradictionsForScope(scope, await readGraphReport(rootDir));
18953
+ const selectedTargets = selectGuidedTargetPages(scope, relatedPages, defaultGuidedSessionQuestions());
18528
18954
  const relatedPageIds = relatedPages.slice(0, 18).map((page) => page.id);
18529
18955
  const relatedNodeIds = graph ? scopedNodeIds(graph, scope.sourceIds).slice(0, 28) : [];
18530
18956
  const projectIds = uniqueStrings4(relatedPages.flatMap((page) => page.projectIds));
@@ -18550,6 +18976,17 @@ async function buildSourceGuideStagedPage(rootDir, scope) {
18550
18976
  compiledFrom: scope.sourceIds,
18551
18977
  managedBy: "system",
18552
18978
  confidence: 0.8
18979
+ },
18980
+ frontmatter: {
18981
+ profile_presets: config.profile.presets,
18982
+ source_type: scopeSourceType(scope, scopeManifests),
18983
+ occurred_at: scopeOccurredAt(scopeManifests),
18984
+ participants: scopeParticipants(scopeManifests),
18985
+ container_title: scopeContainerTitle(scopeManifests),
18986
+ conversation_id: scopeConversationId(scopeManifests),
18987
+ question_state: "answered",
18988
+ canonical_targets: selectedTargets.map((page) => page.path),
18989
+ evidence_state: contradictions.length ? "conflicting" : selectedTargets.some((page) => page.sourceIds.some((sourceId) => !scope.sourceIds.includes(sourceId))) ? "reinforcing" : selectedTargets.length ? "new" : "needs_judgment"
18553
18990
  }
18554
18991
  });
18555
18992
  return { page: output.page, content: output.content };
@@ -18563,40 +19000,378 @@ async function stageSourceReviewForScope(rootDir, scope) {
18563
19000
  return {
18564
19001
  sourceId: scope.id,
18565
19002
  pageId: output.page.id,
18566
- reviewPath: path25.join(approval.approvalDir, "wiki", output.page.path),
19003
+ reviewPath: path26.join(approval.approvalDir, "wiki", output.page.path),
18567
19004
  staged: true,
18568
19005
  approvalId: approval.approvalId,
18569
19006
  approvalDir: approval.approvalDir
18570
19007
  };
18571
19008
  }
18572
- async function stageSourceGuideForScope(rootDir, scope) {
18573
- const briefPath = scope.briefPath ?? await writeSourceBriefForScope(rootDir, scope) ?? void 0;
19009
+ function nextGuidedSourceSessionId(scope) {
19010
+ return `source-session-${slugify(scope.id)}-${sha256(`${scope.id}:${(/* @__PURE__ */ new Date()).toISOString()}`).slice(0, 8)}`;
19011
+ }
19012
+ function shouldReuseGuidedSourceSession(session) {
19013
+ return Boolean(session && session.status === "awaiting_input");
19014
+ }
19015
+ function questionAnswer(questions, id, fallback) {
19016
+ return normalizeGuidedAnswerValue(questions.find((question) => question.id === id)?.answer) ?? fallback;
19017
+ }
19018
+ async function prepareGuidedSourceSession(rootDir, scope, answers) {
19019
+ const existing = await findLatestGuidedSourceSessionByScope(rootDir, scope.id);
19020
+ const now = (/* @__PURE__ */ new Date()).toISOString();
19021
+ const session = shouldReuseGuidedSourceSession(existing) ? {
19022
+ ...existing,
19023
+ scopeTitle: scope.title,
19024
+ sourceIds: scope.sourceIds,
19025
+ kind: scope.kind,
19026
+ questions: mergeGuidedSessionQuestions(existing.questions, answers),
19027
+ updatedAt: now
19028
+ } : {
19029
+ sessionId: nextGuidedSourceSessionId(scope),
19030
+ scopeId: scope.id,
19031
+ scopeTitle: scope.title,
19032
+ sourceIds: scope.sourceIds,
19033
+ kind: scope.kind,
19034
+ status: "awaiting_input",
19035
+ createdAt: now,
19036
+ updatedAt: now,
19037
+ questions: mergeGuidedSessionQuestions(defaultGuidedSessionQuestions(), answers),
19038
+ briefPath: scope.briefPath,
19039
+ targetedPagePaths: [],
19040
+ stagedUpdatePaths: []
19041
+ };
19042
+ const statePath = await guidedSourceSessionStatePath(rootDir, session.sessionId);
19043
+ return { session, statePath };
19044
+ }
19045
+ async function buildSourceSessionSavedPage(rootDir, scope, session) {
19046
+ const { config, paths } = await loadVaultConfig(rootDir);
19047
+ let graph = await readJsonFile(paths.graphPath);
19048
+ if (!graph) {
19049
+ await compileVault(rootDir, {});
19050
+ graph = await readJsonFile(paths.graphPath);
19051
+ }
19052
+ const scopeManifests = manifestsForScope(graph, scope);
19053
+ const sourcePages = graph ? scopedSourcePages(graph, scope.sourceIds) : [];
19054
+ const analyses = await loadSourceAnalyses(rootDir, scope.sourceIds);
19055
+ const report = await readGraphReport(rootDir);
19056
+ const contradictions = findContradictionsForScope(scope, report);
19057
+ const relatedPageIds = uniqueStrings4([
19058
+ ...sourcePages.slice(0, 18).map((page) => page.id),
19059
+ ...session.targetedPagePaths.map((relativePath) => {
19060
+ const page = graph?.pages.find((candidate) => candidate.path === relativePath);
19061
+ return page?.id ?? "";
19062
+ })
19063
+ ]);
19064
+ const relatedNodeIds = graph ? scopedNodeIds(graph, scope.sourceIds).slice(0, 28) : [];
19065
+ const projectIds = uniqueStrings4(sourcePages.flatMap((page) => page.projectIds));
19066
+ const evidenceState = contradictions.length > 0 ? "conflicting" : session.targetedPagePaths.some(
19067
+ (targetPath) => sourcePages.some((page) => page.path === targetPath && page.sourceIds.some((sourceId) => !scope.sourceIds.includes(sourceId)))
19068
+ ) ? "reinforcing" : session.targetedPagePaths.length ? "new" : "needs_judgment";
19069
+ const relativeBriefPath = session.briefPath && path26.isAbsolute(session.briefPath) ? path26.relative(paths.wikiDir, session.briefPath) : session.briefPath;
19070
+ const sessionMarkdown = [
19071
+ `# Guided Session: ${scope.title}`,
19072
+ "",
19073
+ `Status: \`${session.status}\``,
19074
+ `Session ID: \`${session.sessionId}\``,
19075
+ ...session.approvalId ? [`Approval Bundle: \`${session.approvalId}\``] : [],
19076
+ ...relativeBriefPath ? [`Brief: \`${relativeBriefPath}\``] : [],
19077
+ "",
19078
+ "## What This Source Is",
19079
+ "",
19080
+ ...analyses.length ? analyses.slice(0, 6).map((analysis) => `- ${analysis.title}: ${analysis.summary}`) : ["- Awaiting compile context."],
19081
+ "",
19082
+ "## Guided Questions",
19083
+ "",
19084
+ ...session.questions.flatMap((question) => [`### ${question.prompt}`, "", question.answer ?? "_Awaiting input._", ""]),
19085
+ "## Proposed Wiki Targets",
19086
+ "",
19087
+ ...session.targetedPagePaths.length ? session.targetedPagePaths.map((targetPath) => `- [[${targetPath.replace(/\.md$/, "")}]]`) : ["- No canonical update targets selected yet."],
19088
+ "",
19089
+ "## Conflicts And Judgment Calls",
19090
+ "",
19091
+ ...contradictions.length ? contradictions.map((contradiction) => `- ${contradiction.claimA} / ${contradiction.claimB}`) : ["- No contradictions are currently flagged for this source scope."],
19092
+ "",
19093
+ "## Follow-up Questions",
19094
+ "",
19095
+ ...(() => {
19096
+ const followups = questionAnswer(session.questions, "followups", "");
19097
+ if (followups) {
19098
+ return followups.split(/\n+/).map((line) => line.trim()).filter(Boolean).map((line) => `- ${line.replace(/^-+\s*/, "")}`);
19099
+ }
19100
+ const analysisQuestions = uniqueStrings4(analyses.flatMap((analysis) => analysis.questions)).slice(0, 6);
19101
+ return analysisQuestions.length ? analysisQuestions.map((question) => `- ${question}`) : ["- No follow-up questions recorded yet."];
19102
+ })(),
19103
+ "",
19104
+ "## Related Artifacts",
19105
+ "",
19106
+ `- [[outputs/source-briefs/${scope.id}|Source Brief]]`,
19107
+ `- [[outputs/source-reviews/${scope.id}|Source Review]]`,
19108
+ `- [[outputs/source-guides/${scope.id}|Source Guide]]`,
19109
+ ""
19110
+ ].join("\n");
19111
+ const now = (/* @__PURE__ */ new Date()).toISOString();
19112
+ const output = buildOutputPage({
19113
+ title: `Guided Session: ${scope.title}`,
19114
+ question: `Guided Session ${scope.title}`,
19115
+ answer: sessionMarkdown,
19116
+ citations: scope.sourceIds,
19117
+ schemaHash: graph?.generatedAt ?? "",
19118
+ outputFormat: "report",
19119
+ relatedPageIds,
19120
+ relatedNodeIds,
19121
+ relatedSourceIds: scope.sourceIds,
19122
+ projectIds,
19123
+ extraTags: ["source-session", "guided-session"],
19124
+ origin: "query",
19125
+ slug: `source-sessions/${scope.id}`,
19126
+ metadata: {
19127
+ status: "active",
19128
+ createdAt: now,
19129
+ updatedAt: now,
19130
+ compiledFrom: scope.sourceIds,
19131
+ managedBy: "system",
19132
+ confidence: 0.81
19133
+ },
19134
+ frontmatter: {
19135
+ profile_presets: config.profile.presets,
19136
+ source_type: scopeSourceType(scope, scopeManifests),
19137
+ occurred_at: scopeOccurredAt(scopeManifests),
19138
+ participants: scopeParticipants(scopeManifests),
19139
+ container_title: scopeContainerTitle(scopeManifests),
19140
+ conversation_id: scopeConversationId(scopeManifests),
19141
+ session_status: session.status,
19142
+ question_state: questionStateForSession(session),
19143
+ canonical_targets: session.targetedPagePaths,
19144
+ evidence_state: evidenceState
19145
+ }
19146
+ });
19147
+ return { page: output.page, content: output.content };
19148
+ }
19149
+ async function persistSourceSessionPage(rootDir, scope, session) {
19150
+ const { paths } = await loadVaultConfig(rootDir);
19151
+ const output = await buildSourceSessionSavedPage(rootDir, scope, session);
19152
+ const absolutePath = path26.join(paths.wikiDir, output.page.path);
19153
+ await ensureDir(path26.dirname(absolutePath));
19154
+ await fs22.writeFile(absolutePath, output.content, "utf8");
19155
+ return { pageId: output.page.id, sessionPath: absolutePath };
19156
+ }
19157
+ async function buildGuidedUpdatePages(rootDir, scope, session) {
19158
+ const { config, paths } = await loadVaultConfig(rootDir);
19159
+ let graph = await readJsonFile(paths.graphPath);
19160
+ if (!graph) {
19161
+ await compileVault(rootDir, {});
19162
+ graph = await readJsonFile(paths.graphPath);
19163
+ }
19164
+ if (!graph) {
19165
+ return [];
19166
+ }
19167
+ const sourcePages = scopedSourcePages(graph, scope.sourceIds);
19168
+ const scopeManifests = manifestsForScope(graph, scope);
19169
+ const analyses = await loadSourceAnalyses(rootDir, scope.sourceIds);
19170
+ const report = await readGraphReport(rootDir);
19171
+ const contradictions = findContradictionsForScope(scope, report);
19172
+ const selectedTargets = selectGuidedTargetPages(scope, sourcePages, session.questions);
19173
+ const useCanonicalTargets = config.profile.guidedSessionMode === "canonical_review" && selectedTargets.length > 0;
19174
+ const targetPages = useCanonicalTargets ? selectedTargets : [selectedTargets[0] ?? null];
19175
+ session.targetedPagePaths = uniqueStrings4(
19176
+ useCanonicalTargets ? selectedTargets.map((page) => page.path) : selectedTargets.length ? selectedTargets.map((page) => page.path) : session.targetedPagePaths
19177
+ );
19178
+ return await Promise.all(
19179
+ targetPages.map(async (targetPage) => {
19180
+ const evidenceState = classifyGuidedEvidenceState(scope, targetPage, contradictions);
19181
+ const relativePath = useCanonicalTargets && targetPage ? targetPage.path : targetPage ? insightRelativePathForTarget(targetPage, scope) : `insights/topics/${slugify(scope.title)}.md`;
19182
+ const absolutePath = path26.join(paths.wikiDir, relativePath);
19183
+ const existingContent = await fileExists(absolutePath) ? await fs22.readFile(absolutePath, "utf8") : "";
19184
+ const parsed = existingContent ? matter10(existingContent) : { data: {}, content: "" };
19185
+ const existingData = parsed.data;
19186
+ const existingSourceIds = Array.isArray(existingData.source_ids) ? existingData.source_ids.filter((value) => typeof value === "string") : [];
19187
+ const existingProjectIds = Array.isArray(existingData.project_ids) ? existingData.project_ids.filter((value) => typeof value === "string") : [];
19188
+ const existingNodeIds = Array.isArray(existingData.node_ids) ? existingData.node_ids.filter((value) => typeof value === "string") : [];
19189
+ const existingBacklinks = Array.isArray(existingData.backlinks) ? existingData.backlinks.filter((value) => typeof value === "string") : [];
19190
+ const createdAt = typeof existingData.created_at === "string" && existingData.created_at.trim() ? existingData.created_at : (/* @__PURE__ */ new Date()).toISOString();
19191
+ const title = typeof existingData.title === "string" && existingData.title.trim() || (useCanonicalTargets && targetPage ? targetPage.title : targetPage ? insightTitleForTarget(targetPage, scope) : `${scope.title} Notes`);
19192
+ const baseBody = parsed.content.trim() ? parsed.content.trim() : [
19193
+ `# ${title}`,
19194
+ "",
19195
+ useCanonicalTargets ? "Canonical page maintained by SwarmVault. Guided sessions stage replaceable update blocks here for approval." : "Human-curated insight page. Guided sessions stage replaceable update blocks here.",
19196
+ ""
19197
+ ].join("\n");
19198
+ const importance = questionAnswer(
19199
+ session.questions,
19200
+ "importance",
19201
+ "Capture the most important new ideas from this source before treating them as canonical."
19202
+ );
19203
+ const exclude = questionAnswer(
19204
+ session.questions,
19205
+ "exclude",
19206
+ "Keep uncertain or incidental details provisional until they matter to the research thread."
19207
+ );
19208
+ const conflictNotes = questionAnswer(
19209
+ session.questions,
19210
+ "conflicts",
19211
+ contradictions.length ? "Review the conflicting evidence before accepting any canonical summary changes." : "No explicit conflicts were called out."
19212
+ );
19213
+ const followups = questionAnswer(session.questions, "followups", "Track follow-up questions on the source session page.");
19214
+ const updateBlock = [
19215
+ `## Guided Session Update: ${scope.title}`,
19216
+ "",
19217
+ `Evidence State: \`${evidenceState}\``,
19218
+ `Session: [[outputs/source-sessions/${scope.id}|Guided Session]]`,
19219
+ `Source Guide: [[outputs/source-guides/${scope.id}|Source Guide]]`,
19220
+ "",
19221
+ "### What Matters Now",
19222
+ "",
19223
+ importance,
19224
+ "",
19225
+ "### Proposed Integration",
19226
+ "",
19227
+ targetPage ? `- Fold the strongest source-backed takeaways into [[${targetPage.path.replace(/\.md$/, "")}|${targetPage.title}]].` : `- Start a durable topic note for ${scope.title}.`,
19228
+ ...analyses.slice(0, 5).map((analysis) => `- ${truncate(normalizeWhitespace(analysis.summary), 180)}`),
19229
+ "",
19230
+ "### Keep Provisional Or Out",
19231
+ "",
19232
+ exclude,
19233
+ "",
19234
+ "### Reinforcing Or Conflicting Notes",
19235
+ "",
19236
+ conflictNotes,
19237
+ ...contradictions.length ? ["", ...contradictions.slice(0, 4).map((contradiction) => `- ${contradiction.claimA} / ${contradiction.claimB}`)] : [],
19238
+ "",
19239
+ "### Follow-up Questions",
19240
+ "",
19241
+ followups,
19242
+ ""
19243
+ ].join("\n");
19244
+ const nextBody = replaceMarkedSection(baseBody, scope.id, updateBlock);
19245
+ const content = matter10.stringify(
19246
+ `${nextBody.trimEnd()}
19247
+ `,
19248
+ JSON.parse(
19249
+ JSON.stringify({
19250
+ ...existingData,
19251
+ page_id: typeof existingData.page_id === "string" && existingData.page_id.trim() || (useCanonicalTargets && targetPage ? targetPage.id : `insight:${slugify(relativePath.replace(/\.md$/, ""))}`),
19252
+ kind: useCanonicalTargets && targetPage ? targetPage.kind : "insight",
19253
+ title,
19254
+ tags: uniqueStrings4([
19255
+ ...Array.isArray(existingData.tags) ? existingData.tags.filter((value) => typeof value === "string") : [],
19256
+ ...useCanonicalTargets ? ["guided-session", `guided/${targetPage?.kind ?? "page"}`] : insightTagsForTarget(targetPage)
19257
+ ]),
19258
+ source_ids: uniqueStrings4([...existingSourceIds, ...scope.sourceIds]),
19259
+ project_ids: uniqueStrings4([...existingProjectIds, ...targetPage?.projectIds ?? []]),
19260
+ node_ids: uniqueStrings4([...existingNodeIds, ...targetPage?.nodeIds ?? []]),
19261
+ freshness: "fresh",
19262
+ status: existingData.status === "archived" ? "archived" : "active",
19263
+ confidence: 0.83,
19264
+ created_at: createdAt,
19265
+ updated_at: (/* @__PURE__ */ new Date()).toISOString(),
19266
+ compiled_from: uniqueStrings4([
19267
+ ...Array.isArray(existingData.compiled_from) ? existingData.compiled_from.filter((value) => typeof value === "string") : [],
19268
+ ...scope.sourceIds
19269
+ ]),
19270
+ managed_by: typeof existingData.managed_by === "string" && (existingData.managed_by === "human" || existingData.managed_by === "system") ? existingData.managed_by : useCanonicalTargets ? "system" : "human",
19271
+ backlinks: uniqueStrings4([
19272
+ ...existingBacklinks,
19273
+ ...targetPage ? [targetPage.id] : [],
19274
+ `output:source-sessions/${scope.id}`,
19275
+ `output:source-guides/${scope.id}`
19276
+ ]),
19277
+ schema_hash: typeof existingData.schema_hash === "string" ? existingData.schema_hash : "",
19278
+ source_hashes: existingData.source_hashes && typeof existingData.source_hashes === "object" ? existingData.source_hashes : {},
19279
+ source_semantic_hashes: existingData.source_semantic_hashes && typeof existingData.source_semantic_hashes === "object" ? existingData.source_semantic_hashes : {},
19280
+ profile_presets: config.profile.presets,
19281
+ source_type: scopeSourceType(scope, scopeManifests),
19282
+ occurred_at: scopeOccurredAt(scopeManifests),
19283
+ participants: scopeParticipants(scopeManifests),
19284
+ container_title: scopeContainerTitle(scopeManifests),
19285
+ conversation_id: scopeConversationId(scopeManifests),
19286
+ session_status: session.status,
19287
+ question_state: questionStateForSession(session),
19288
+ canonical_targets: useCanonicalTargets ? selectedTargets.map((page2) => page2.path) : [],
19289
+ evidence_state: evidenceState
19290
+ })
19291
+ )
19292
+ );
19293
+ const page = parseStoredPage(relativePath, content, {
19294
+ createdAt,
19295
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
19296
+ });
19297
+ if (!useCanonicalTargets && !selectedTargets.length) {
19298
+ session.targetedPagePaths = uniqueStrings4([...session.targetedPagePaths, relativePath]);
19299
+ }
19300
+ return { page, content, label: "guided-update" };
19301
+ })
19302
+ );
19303
+ }
19304
+ async function stageSourceGuideForScope(rootDir, scope, options = {}) {
19305
+ const { session, statePath } = await prepareGuidedSourceSession(rootDir, scope, options.answers);
19306
+ const briefPath = scope.briefPath ?? session.briefPath ?? await writeSourceBriefForScope(rootDir, scope) ?? void 0;
19307
+ session.briefPath = briefPath;
18574
19308
  if (briefPath) {
18575
19309
  await refreshVaultAfterOutputSave(rootDir);
18576
19310
  }
19311
+ if (answeredGuidedSessionQuestions(session.questions).length === 0) {
19312
+ session.status = "awaiting_input";
19313
+ const persisted2 = await persistSourceSessionPage(rootDir, scope, session);
19314
+ session.sessionPath = persisted2.sessionPath;
19315
+ await writeGuidedSourceSession(rootDir, session);
19316
+ await refreshVaultAfterOutputSave(rootDir);
19317
+ return {
19318
+ sourceId: scope.id,
19319
+ sessionId: session.sessionId,
19320
+ sessionPath: persisted2.sessionPath,
19321
+ sessionStatePath: statePath,
19322
+ status: session.status,
19323
+ questions: session.questions,
19324
+ awaitingInput: true,
19325
+ targetedPagePaths: session.targetedPagePaths,
19326
+ stagedUpdatePaths: session.stagedUpdatePaths,
19327
+ briefPath,
19328
+ staged: false
19329
+ };
19330
+ }
19331
+ session.status = "ready_to_stage";
19332
+ await writeGuidedSourceSession(rootDir, session);
18577
19333
  const reviewOutput = await buildSourceReviewStagedPage(rootDir, scope);
18578
19334
  const guideOutput = await buildSourceGuideStagedPage(rootDir, {
18579
19335
  ...scope,
18580
19336
  briefPath
18581
19337
  });
19338
+ const guidedUpdates = await buildGuidedUpdatePages(rootDir, scope, session);
19339
+ session.stagedUpdatePaths = guidedUpdates.map((item) => item.page.path);
18582
19340
  const approval = await stageGeneratedOutputPages(
18583
19341
  rootDir,
18584
19342
  [
18585
19343
  { page: reviewOutput.page, content: reviewOutput.content, label: "source-review" },
18586
- { page: guideOutput.page, content: guideOutput.content, label: "source-guide" }
19344
+ { page: guideOutput.page, content: guideOutput.content, label: "source-guide" },
19345
+ ...guidedUpdates
18587
19346
  ],
18588
19347
  {
18589
- bundleType: "guided_source",
18590
- title: `Guided Source: ${scope.title}`
19348
+ bundleType: "guided_session",
19349
+ title: `Guided Session: ${scope.title}`,
19350
+ sourceSessionId: session.sessionId
18591
19351
  }
18592
19352
  );
19353
+ session.status = "staged";
19354
+ session.reviewPath = path26.join(approval.approvalDir, "wiki", reviewOutput.page.path);
19355
+ session.guidePath = path26.join(approval.approvalDir, "wiki", guideOutput.page.path);
19356
+ session.approvalId = approval.approvalId;
19357
+ session.approvalDir = approval.approvalDir;
19358
+ const persisted = await persistSourceSessionPage(rootDir, scope, session);
19359
+ session.sessionPath = persisted.sessionPath;
19360
+ await writeGuidedSourceSession(rootDir, session);
18593
19361
  await refreshVaultAfterOutputSave(rootDir);
18594
19362
  return {
18595
19363
  sourceId: scope.id,
18596
19364
  pageId: guideOutput.page.id,
18597
- guidePath: path25.join(approval.approvalDir, "wiki", guideOutput.page.path),
19365
+ guidePath: session.guidePath,
18598
19366
  reviewPageId: reviewOutput.page.id,
18599
- reviewPath: path25.join(approval.approvalDir, "wiki", reviewOutput.page.path),
19367
+ reviewPath: session.reviewPath,
19368
+ sessionId: session.sessionId,
19369
+ sessionPath: persisted.sessionPath,
19370
+ sessionStatePath: statePath,
19371
+ status: session.status,
19372
+ questions: session.questions,
19373
+ targetedPagePaths: session.targetedPagePaths,
19374
+ stagedUpdatePaths: session.stagedUpdatePaths,
18600
19375
  briefPath,
18601
19376
  staged: true,
18602
19377
  approvalId: approval.approvalId,
@@ -18612,53 +19387,82 @@ function scopeFromManagedSource(source) {
18612
19387
  briefPath: source.briefPath
18613
19388
  };
18614
19389
  }
18615
- async function reviewSourceScope(rootDir, scope) {
18616
- return await stageSourceReviewForScope(rootDir, scope);
18617
- }
18618
- async function guideSourceScope(rootDir, scope) {
18619
- return await stageSourceGuideForScope(rootDir, scope);
18620
- }
18621
- async function reviewManagedSource(rootDir, id) {
18622
- const managedSources = await loadManagedSources(rootDir);
18623
- const managedSource = managedSources.find((source) => source.id === id);
18624
- if (managedSource) {
18625
- if (!await loadVaultConfig(rootDir).then(({ paths }) => fileExists(paths.graphPath))) {
18626
- await compileVault(rootDir, {});
18627
- }
18628
- return await stageSourceReviewForScope(rootDir, scopeFromManagedSource(managedSource));
18629
- }
18630
- const manifests = await listManifests(rootDir);
18631
- const manifest = manifests.find((candidate) => candidate.sourceId === id);
18632
- if (!manifest) {
18633
- throw new Error(`Managed source or source id not found: ${id}`);
18634
- }
18635
- return await stageSourceReviewForScope(rootDir, {
18636
- id: manifest.sourceGroupId ?? manifest.sourceId,
19390
+ function scopeFromManifest(manifest, manifests) {
19391
+ const groupId = manifest.sourceGroupId ?? manifest.sourceId;
19392
+ return {
19393
+ id: groupId,
18637
19394
  title: manifest.sourceGroupTitle ?? manifest.title,
18638
19395
  sourceIds: manifest.sourceGroupId ? manifests.filter((candidate) => candidate.sourceGroupId === manifest.sourceGroupId).map((candidate) => candidate.sourceId) : [manifest.sourceId],
18639
19396
  kind: manifest.sourceKind
18640
- });
19397
+ };
18641
19398
  }
18642
- async function guideManagedSource(rootDir, id) {
19399
+ async function resolveSourceScope(rootDir, id) {
18643
19400
  const managedSources = await loadManagedSources(rootDir);
18644
19401
  const managedSource = managedSources.find((source) => source.id === id);
18645
19402
  if (managedSource) {
18646
- if (!await loadVaultConfig(rootDir).then(({ paths }) => fileExists(paths.graphPath))) {
18647
- await compileVault(rootDir, {});
18648
- }
18649
- return await stageSourceGuideForScope(rootDir, scopeFromManagedSource(managedSource));
19403
+ return scopeFromManagedSource(managedSource);
19404
+ }
19405
+ const latestSession = await findLatestGuidedSourceSessionByScope(rootDir, id);
19406
+ if (latestSession) {
19407
+ return {
19408
+ id: latestSession.scopeId,
19409
+ title: latestSession.scopeTitle,
19410
+ sourceIds: latestSession.sourceIds
19411
+ };
18650
19412
  }
18651
19413
  const manifests = await listManifests(rootDir);
18652
- const manifest = manifests.find((candidate) => candidate.sourceId === id);
19414
+ const manifest = manifests.find((candidate) => candidate.sourceId === id) ?? manifests.find((candidate) => candidate.sourceGroupId === id);
18653
19415
  if (!manifest) {
19416
+ return null;
19417
+ }
19418
+ return scopeFromManifest(manifest, manifests);
19419
+ }
19420
+ async function reviewSourceScope(rootDir, scope) {
19421
+ return await stageSourceReviewForScope(rootDir, scope);
19422
+ }
19423
+ async function guideSourceScope(rootDir, scope, options = {}) {
19424
+ return await stageSourceGuideForScope(rootDir, scope, options);
19425
+ }
19426
+ async function reviewManagedSource(rootDir, id) {
19427
+ const scope = await resolveSourceScope(rootDir, id);
19428
+ if (!scope) {
18654
19429
  throw new Error(`Managed source or source id not found: ${id}`);
18655
19430
  }
18656
- return await stageSourceGuideForScope(rootDir, {
18657
- id: manifest.sourceGroupId ?? manifest.sourceId,
18658
- title: manifest.sourceGroupTitle ?? manifest.title,
18659
- sourceIds: manifest.sourceGroupId ? manifests.filter((candidate) => candidate.sourceGroupId === manifest.sourceGroupId).map((candidate) => candidate.sourceId) : [manifest.sourceId],
18660
- kind: manifest.sourceKind
18661
- });
19431
+ if (!await loadVaultConfig(rootDir).then(({ paths }) => fileExists(paths.graphPath))) {
19432
+ await compileVault(rootDir, {});
19433
+ }
19434
+ return await stageSourceReviewForScope(rootDir, scope);
19435
+ }
19436
+ async function guideManagedSource(rootDir, id, options = {}) {
19437
+ const scope = await resolveSourceScope(rootDir, id);
19438
+ if (!scope) {
19439
+ throw new Error(`Managed source or source id not found: ${id}`);
19440
+ }
19441
+ if (!await loadVaultConfig(rootDir).then(({ paths }) => fileExists(paths.graphPath))) {
19442
+ await compileVault(rootDir, {});
19443
+ }
19444
+ return await stageSourceGuideForScope(rootDir, scope, options);
19445
+ }
19446
+ async function resumeSourceSession(rootDir, id, options = {}) {
19447
+ const existingSession = await readGuidedSourceSession(rootDir, id);
19448
+ if (existingSession) {
19449
+ return await stageSourceGuideForScope(
19450
+ rootDir,
19451
+ {
19452
+ id: existingSession.scopeId,
19453
+ title: existingSession.scopeTitle,
19454
+ sourceIds: existingSession.sourceIds,
19455
+ kind: existingSession.kind,
19456
+ briefPath: existingSession.briefPath
19457
+ },
19458
+ options
19459
+ );
19460
+ }
19461
+ const scope = await resolveSourceScope(rootDir, id);
19462
+ if (!scope) {
19463
+ throw new Error(`Managed source, source scope, or guided session not found: ${id}`);
19464
+ }
19465
+ return await stageSourceGuideForScope(rootDir, scope, options);
18662
19466
  }
18663
19467
  function shouldCompile(changedSources, graphExists, compileRequested) {
18664
19468
  return compileRequested && (!graphExists || changedSources.length > 0);
@@ -18677,7 +19481,7 @@ async function addManagedSource(rootDir, input, options = {}) {
18677
19481
  const existing = sources.find((candidate) => matchesManagedSourceSpec(candidate, resolved));
18678
19482
  const now = (/* @__PURE__ */ new Date()).toISOString();
18679
19483
  const source = existing ?? {
18680
- id: resolved.kind === "directory" || resolved.kind === "file" ? stableManagedSourceId(resolved.kind, path25.resolve(resolved.path), resolved.title) : stableManagedSourceId(resolved.kind, resolved.url, resolved.title),
19484
+ id: resolved.kind === "directory" || resolved.kind === "file" ? stableManagedSourceId(resolved.kind, path26.resolve(resolved.path), resolved.title) : stableManagedSourceId(resolved.kind, resolved.url, resolved.title),
18681
19485
  kind: resolved.kind,
18682
19486
  title: resolved.title,
18683
19487
  path: resolved.kind === "directory" || resolved.kind === "file" ? resolved.path : void 0,
@@ -18712,10 +19516,14 @@ async function addManagedSource(rootDir, input, options = {}) {
18712
19516
  const nextSources = existing ? sources.map((candidate) => candidate.id === nextSource.id ? nextSource : candidate) : [...sources, nextSource];
18713
19517
  await saveManagedSources(rootDir, nextSources);
18714
19518
  const review = reviewRequested && nextSource.status === "ready" ? await stageSourceReviewForScope(rootDir, scopeFromManagedSource(nextSource)) : void 0;
18715
- const guide = guideRequested && nextSource.status === "ready" ? await stageSourceGuideForScope(rootDir, {
18716
- ...scopeFromManagedSource(nextSource),
18717
- briefPath: nextSource.briefPath
18718
- }) : void 0;
19519
+ const guide = guideRequested && nextSource.status === "ready" ? await stageSourceGuideForScope(
19520
+ rootDir,
19521
+ {
19522
+ ...scopeFromManagedSource(nextSource),
19523
+ briefPath: nextSource.briefPath
19524
+ },
19525
+ { answers: options.guideAnswers }
19526
+ ) : void 0;
18719
19527
  return {
18720
19528
  source: nextSource,
18721
19529
  compile,
@@ -18769,10 +19577,14 @@ async function reloadManagedSources(rootDir, options = {}) {
18769
19577
  ) : [];
18770
19578
  const guides = guideRequested ? await Promise.all(
18771
19579
  nextSources.filter((source) => selected.some((candidate) => candidate.id === source.id)).filter((source) => source.status === "ready").map(
18772
- async (source) => await stageSourceGuideForScope(rootDir, {
18773
- ...scopeFromManagedSource(source),
18774
- briefPath: source.briefPath
18775
- })
19580
+ async (source) => await stageSourceGuideForScope(
19581
+ rootDir,
19582
+ {
19583
+ ...scopeFromManagedSource(source),
19584
+ briefPath: source.briefPath
19585
+ },
19586
+ { answers: options.guideAnswers }
19587
+ )
18776
19588
  )
18777
19589
  ) : [];
18778
19590
  return {
@@ -18794,17 +19606,17 @@ async function deleteManagedSource(rootDir, id) {
18794
19606
  sources.filter((source) => source.id !== id)
18795
19607
  );
18796
19608
  const workingDir = await managedSourceWorkingDir(rootDir, id);
18797
- await fs21.rm(workingDir, { recursive: true, force: true });
19609
+ await fs22.rm(workingDir, { recursive: true, force: true });
18798
19610
  return { removed: target };
18799
19611
  }
18800
19612
 
18801
19613
  // src/viewer.ts
18802
19614
  import { execFile } from "child_process";
18803
- import fs22 from "fs/promises";
19615
+ import fs23 from "fs/promises";
18804
19616
  import http from "http";
18805
- import path27 from "path";
19617
+ import path28 from "path";
18806
19618
  import { promisify } from "util";
18807
- import matter10 from "gray-matter";
19619
+ import matter11 from "gray-matter";
18808
19620
  import mime2 from "mime-types";
18809
19621
 
18810
19622
  // src/graph-presentation.ts
@@ -18926,7 +19738,7 @@ function buildViewerGraphArtifact(graph, options = {}) {
18926
19738
  }
18927
19739
 
18928
19740
  // src/watch.ts
18929
- import path26 from "path";
19741
+ import path27 from "path";
18930
19742
  import process3 from "process";
18931
19743
  import chokidar from "chokidar";
18932
19744
  var MAX_BACKOFF_MS = 3e4;
@@ -18934,15 +19746,15 @@ var BACKOFF_THRESHOLD = 3;
18934
19746
  var CRITICAL_THRESHOLD = 10;
18935
19747
  var REPO_WATCH_IGNORES = /* @__PURE__ */ new Set([".git", ".venv"]);
18936
19748
  function withinRoot3(rootPath, targetPath) {
18937
- const relative = path26.relative(rootPath, targetPath);
18938
- return relative === "" || !relative.startsWith("..") && !path26.isAbsolute(relative);
19749
+ const relative = path27.relative(rootPath, targetPath);
19750
+ return relative === "" || !relative.startsWith("..") && !path27.isAbsolute(relative);
18939
19751
  }
18940
19752
  function hasIgnoredRepoSegment(baseDir, targetPath) {
18941
- const relativePath = path26.relative(baseDir, targetPath);
19753
+ const relativePath = path27.relative(baseDir, targetPath);
18942
19754
  if (!relativePath || relativePath.startsWith("..")) {
18943
19755
  return false;
18944
19756
  }
18945
- return relativePath.split(path26.sep).some((segment) => REPO_WATCH_IGNORES.has(segment));
19757
+ return relativePath.split(path27.sep).some((segment) => REPO_WATCH_IGNORES.has(segment));
18946
19758
  }
18947
19759
  function workspaceIgnoreRoots(rootDir, paths) {
18948
19760
  return [
@@ -18951,16 +19763,16 @@ function workspaceIgnoreRoots(rootDir, paths) {
18951
19763
  paths.stateDir,
18952
19764
  paths.agentDir,
18953
19765
  paths.inboxDir,
18954
- path26.join(rootDir, ".claude"),
18955
- path26.join(rootDir, ".cursor"),
18956
- path26.join(rootDir, ".obsidian")
18957
- ].map((candidate) => path26.resolve(candidate));
19766
+ path27.join(rootDir, ".claude"),
19767
+ path27.join(rootDir, ".cursor"),
19768
+ path27.join(rootDir, ".obsidian")
19769
+ ].map((candidate) => path27.resolve(candidate));
18958
19770
  }
18959
19771
  async function resolveWatchTargets(rootDir, paths, options) {
18960
- const targets = /* @__PURE__ */ new Set([path26.resolve(paths.inboxDir)]);
19772
+ const targets = /* @__PURE__ */ new Set([path27.resolve(paths.inboxDir)]);
18961
19773
  if (options.repo) {
18962
19774
  for (const repoRoot of await listTrackedRepoRoots(rootDir)) {
18963
- targets.add(path26.resolve(repoRoot));
19775
+ targets.add(path27.resolve(repoRoot));
18964
19776
  }
18965
19777
  }
18966
19778
  return [...targets].sort((left, right) => left.localeCompare(right));
@@ -19090,7 +19902,7 @@ async function watchVault(rootDir, options = {}) {
19090
19902
  const { paths } = await initWorkspace(rootDir);
19091
19903
  const baseDebounceMs = options.debounceMs ?? 900;
19092
19904
  const ignoredRoots = workspaceIgnoreRoots(rootDir, paths);
19093
- const inboxWatchRoot = path26.resolve(paths.inboxDir);
19905
+ const inboxWatchRoot = path27.resolve(paths.inboxDir);
19094
19906
  let watchTargets = await resolveWatchTargets(rootDir, paths, options);
19095
19907
  let timer;
19096
19908
  let running = false;
@@ -19105,7 +19917,7 @@ async function watchVault(rootDir, options = {}) {
19105
19917
  usePolling: true,
19106
19918
  interval: 100,
19107
19919
  ignored: (targetPath) => {
19108
- const absolutePath = path26.resolve(targetPath);
19920
+ const absolutePath = path27.resolve(targetPath);
19109
19921
  const primaryTarget = watchTargets.filter((watchTarget) => withinRoot3(watchTarget, absolutePath)).sort((left, right) => right.length - left.length)[0] ?? null;
19110
19922
  if (!primaryTarget) {
19111
19923
  return false;
@@ -19294,8 +20106,8 @@ async function watchVault(rootDir, options = {}) {
19294
20106
  }
19295
20107
  };
19296
20108
  const reasonForPath = (targetPath) => {
19297
- const baseDir = watchTargets.filter((watchTarget) => withinRoot3(watchTarget, path26.resolve(targetPath))).sort((left, right) => right.length - left.length)[0] ?? paths.inboxDir;
19298
- return path26.relative(baseDir, targetPath) || ".";
20109
+ const baseDir = watchTargets.filter((watchTarget) => withinRoot3(watchTarget, path27.resolve(targetPath))).sort((left, right) => right.length - left.length)[0] ?? paths.inboxDir;
20110
+ return path27.relative(baseDir, targetPath) || ".";
19299
20111
  };
19300
20112
  watcher.on("add", (filePath) => schedule(`add:${reasonForPath(filePath)}`)).on("change", (filePath) => schedule(`change:${reasonForPath(filePath)}`)).on("unlink", (filePath) => schedule(`unlink:${reasonForPath(filePath)}`)).on("addDir", (dirPath) => schedule(`addDir:${reasonForPath(dirPath)}`)).on("unlinkDir", (dirPath) => schedule(`unlinkDir:${reasonForPath(dirPath)}`)).on("error", (caught) => schedule(`error:${caught instanceof Error ? caught.message : String(caught)}`));
19301
20113
  await new Promise((resolve, reject) => {
@@ -19337,15 +20149,15 @@ async function getWatchStatus(rootDir) {
19337
20149
  var execFileAsync = promisify(execFile);
19338
20150
  async function readViewerPage(rootDir, relativePath) {
19339
20151
  const { paths } = await loadVaultConfig(rootDir);
19340
- const absolutePath = path27.resolve(paths.wikiDir, relativePath);
20152
+ const absolutePath = path28.resolve(paths.wikiDir, relativePath);
19341
20153
  if (!absolutePath.startsWith(paths.wikiDir) || !await fileExists(absolutePath)) {
19342
20154
  return null;
19343
20155
  }
19344
- const raw = await fs22.readFile(absolutePath, "utf8");
19345
- const parsed = matter10(raw);
20156
+ const raw = await fs23.readFile(absolutePath, "utf8");
20157
+ const parsed = matter11(raw);
19346
20158
  return {
19347
20159
  path: relativePath,
19348
- title: typeof parsed.data.title === "string" ? parsed.data.title : path27.basename(relativePath, path27.extname(relativePath)),
20160
+ title: typeof parsed.data.title === "string" ? parsed.data.title : path28.basename(relativePath, path28.extname(relativePath)),
19349
20161
  frontmatter: parsed.data,
19350
20162
  content: parsed.content,
19351
20163
  assets: normalizeOutputAssets(parsed.data.output_assets)
@@ -19353,12 +20165,12 @@ async function readViewerPage(rootDir, relativePath) {
19353
20165
  }
19354
20166
  async function readViewerAsset(rootDir, relativePath) {
19355
20167
  const { paths } = await loadVaultConfig(rootDir);
19356
- const absolutePath = path27.resolve(paths.wikiDir, relativePath);
20168
+ const absolutePath = path28.resolve(paths.wikiDir, relativePath);
19357
20169
  if (!absolutePath.startsWith(paths.wikiDir) || !await fileExists(absolutePath)) {
19358
20170
  return null;
19359
20171
  }
19360
20172
  return {
19361
- buffer: await fs22.readFile(absolutePath),
20173
+ buffer: await fs23.readFile(absolutePath),
19362
20174
  mimeType: mime2.lookup(absolutePath) || "application/octet-stream"
19363
20175
  };
19364
20176
  }
@@ -19381,12 +20193,12 @@ async function readJsonBody(request) {
19381
20193
  return JSON.parse(raw);
19382
20194
  }
19383
20195
  async function ensureViewerDist(viewerDistDir) {
19384
- const indexPath = path27.join(viewerDistDir, "index.html");
20196
+ const indexPath = path28.join(viewerDistDir, "index.html");
19385
20197
  if (await fileExists(indexPath)) {
19386
20198
  return;
19387
20199
  }
19388
- const viewerProjectDir = path27.dirname(viewerDistDir);
19389
- if (await fileExists(path27.join(viewerProjectDir, "package.json"))) {
20200
+ const viewerProjectDir = path28.dirname(viewerDistDir);
20201
+ if (await fileExists(path28.join(viewerProjectDir, "package.json"))) {
19390
20202
  await execFileAsync("pnpm", ["build"], { cwd: viewerProjectDir });
19391
20203
  }
19392
20204
  }
@@ -19408,7 +20220,7 @@ async function startGraphServer(rootDir, port, options = {}) {
19408
20220
  response.end(JSON.stringify({ error: "Graph artifact not found. Run `swarmvault compile` first." }));
19409
20221
  return;
19410
20222
  }
19411
- const reportPath = path27.join(paths.wikiDir, "graph", "report.json");
20223
+ const reportPath = path28.join(paths.wikiDir, "graph", "report.json");
19412
20224
  const report = await readJsonFile(reportPath) ?? null;
19413
20225
  response.writeHead(200, { "content-type": "application/json" });
19414
20226
  response.end(JSON.stringify(buildViewerGraphArtifact(graph, { report, full: options.full ?? false })));
@@ -19468,14 +20280,14 @@ async function startGraphServer(rootDir, port, options = {}) {
19468
20280
  return;
19469
20281
  }
19470
20282
  if (url.pathname === "/api/graph-report") {
19471
- const reportPath = path27.join(paths.wikiDir, "graph", "report.json");
20283
+ const reportPath = path28.join(paths.wikiDir, "graph", "report.json");
19472
20284
  if (!await fileExists(reportPath)) {
19473
20285
  response.writeHead(404, { "content-type": "application/json" });
19474
20286
  response.end(JSON.stringify({ error: "Graph report artifact not found. Run `swarmvault compile` first." }));
19475
20287
  return;
19476
20288
  }
19477
20289
  response.writeHead(200, { "content-type": "application/json" });
19478
- response.end(await fs22.readFile(reportPath, "utf8"));
20290
+ response.end(await fs23.readFile(reportPath, "utf8"));
19479
20291
  return;
19480
20292
  }
19481
20293
  if (url.pathname === "/api/watch-status") {
@@ -19558,8 +20370,8 @@ async function startGraphServer(rootDir, port, options = {}) {
19558
20370
  return;
19559
20371
  }
19560
20372
  const relativePath = url.pathname === "/" ? "index.html" : url.pathname.slice(1);
19561
- const target = path27.join(paths.viewerDistDir, relativePath);
19562
- const fallback = path27.join(paths.viewerDistDir, "index.html");
20373
+ const target = path28.join(paths.viewerDistDir, relativePath);
20374
+ const fallback = path28.join(paths.viewerDistDir, "index.html");
19563
20375
  const filePath = await fileExists(target) ? target : fallback;
19564
20376
  if (!await fileExists(filePath)) {
19565
20377
  response.writeHead(503, { "content-type": "text/plain" });
@@ -19567,7 +20379,7 @@ async function startGraphServer(rootDir, port, options = {}) {
19567
20379
  return;
19568
20380
  }
19569
20381
  response.writeHead(200, { "content-type": mime2.lookup(filePath) || "text/plain" });
19570
- response.end(await fs22.readFile(filePath));
20382
+ response.end(await fs23.readFile(filePath));
19571
20383
  });
19572
20384
  await new Promise((resolve) => {
19573
20385
  server.listen(effectivePort, resolve);
@@ -19594,7 +20406,7 @@ async function exportGraphHtml(rootDir, outputPath, options = {}) {
19594
20406
  throw new Error("Graph artifact not found. Run `swarmvault compile` first.");
19595
20407
  }
19596
20408
  await ensureViewerDist(paths.viewerDistDir);
19597
- const indexPath = path27.join(paths.viewerDistDir, "index.html");
20409
+ const indexPath = path28.join(paths.viewerDistDir, "index.html");
19598
20410
  if (!await fileExists(indexPath)) {
19599
20411
  throw new Error("Viewer build not found. Run `pnpm build` first.");
19600
20412
  }
@@ -19620,17 +20432,17 @@ async function exportGraphHtml(rootDir, outputPath, options = {}) {
19620
20432
  } : null;
19621
20433
  })
19622
20434
  );
19623
- const rawHtml = await fs22.readFile(indexPath, "utf8");
20435
+ const rawHtml = await fs23.readFile(indexPath, "utf8");
19624
20436
  const scriptMatch = rawHtml.match(/<script type="module" crossorigin src="([^"]+)"><\/script>/);
19625
20437
  const styleMatch = rawHtml.match(/<link rel="stylesheet" crossorigin href="([^"]+)">/);
19626
- const scriptPath = scriptMatch?.[1] ? path27.join(paths.viewerDistDir, scriptMatch[1].replace(/^\//, "")) : null;
19627
- const stylePath = styleMatch?.[1] ? path27.join(paths.viewerDistDir, styleMatch[1].replace(/^\//, "")) : null;
20438
+ const scriptPath = scriptMatch?.[1] ? path28.join(paths.viewerDistDir, scriptMatch[1].replace(/^\//, "")) : null;
20439
+ const stylePath = styleMatch?.[1] ? path28.join(paths.viewerDistDir, styleMatch[1].replace(/^\//, "")) : null;
19628
20440
  if (!scriptPath || !await fileExists(scriptPath)) {
19629
20441
  throw new Error("Viewer script bundle not found. Run `pnpm build` first.");
19630
20442
  }
19631
- const script = await fs22.readFile(scriptPath, "utf8");
19632
- const style = stylePath && await fileExists(stylePath) ? await fs22.readFile(stylePath, "utf8") : "";
19633
- const report = await readJsonFile(path27.join(paths.wikiDir, "graph", "report.json"));
20443
+ const script = await fs23.readFile(scriptPath, "utf8");
20444
+ const style = stylePath && await fileExists(stylePath) ? await fs23.readFile(stylePath, "utf8") : "";
20445
+ const report = await readJsonFile(path28.join(paths.wikiDir, "graph", "report.json"));
19634
20446
  const embeddedData = JSON.stringify(
19635
20447
  { graph: buildViewerGraphArtifact(graph, { report, full: options.full ?? false }), pages: pages.filter(Boolean), report },
19636
20448
  null,
@@ -19653,9 +20465,9 @@ async function exportGraphHtml(rootDir, outputPath, options = {}) {
19653
20465
  "</html>",
19654
20466
  ""
19655
20467
  ].filter(Boolean).join("\n");
19656
- await fs22.mkdir(path27.dirname(outputPath), { recursive: true });
19657
- await fs22.writeFile(outputPath, html, "utf8");
19658
- return path27.resolve(outputPath);
20468
+ await fs23.mkdir(path28.dirname(outputPath), { recursive: true });
20469
+ await fs23.writeFile(outputPath, html, "utf8");
20470
+ return path28.resolve(outputPath);
19659
20471
  }
19660
20472
  export {
19661
20473
  acceptApproval,
@@ -19717,6 +20529,7 @@ export {
19717
20529
  rejectApproval,
19718
20530
  reloadManagedSources,
19719
20531
  resolvePaths,
20532
+ resumeSourceSession,
19720
20533
  reviewManagedSource,
19721
20534
  reviewSourceScope,
19722
20535
  runSchedule,