@swarmvaultai/engine 0.1.33 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -21,7 +21,7 @@ import {
21
21
  uniqueBy,
22
22
  writeFileIfChanged,
23
23
  writeJsonFile
24
- } from "./chunk-IAEYFTUS.js";
24
+ } from "./chunk-CWLDFLH2.js";
25
25
 
26
26
  // src/agents.ts
27
27
  import crypto from "crypto";
@@ -1721,8 +1721,8 @@ async function uninstallGitHooks(rootDir) {
1721
1721
  }
1722
1722
 
1723
1723
  // src/ingest.ts
1724
- import fs10 from "fs/promises";
1725
- import path11 from "path";
1724
+ import fs11 from "fs/promises";
1725
+ import path12 from "path";
1726
1726
  import { pathToFileURL } from "url";
1727
1727
  import { Readability } from "@mozilla/readability";
1728
1728
  import matter3 from "gray-matter";
@@ -4724,9 +4724,123 @@ function aggregateManifestSourceClass(manifests, sourceIds) {
4724
4724
  return aggregateSourceClass(sourceIds.map((sourceId) => byId.get(sourceId)));
4725
4725
  }
4726
4726
 
4727
- // src/watch-state.ts
4727
+ // src/source-registry.ts
4728
4728
  import fs9 from "fs/promises";
4729
4729
  import path10 from "path";
4730
+ var MANAGED_SOURCES_VERSION = 1;
4731
+ function repoRootFromManifest(manifest) {
4732
+ if (manifest.originType !== "file" || !manifest.originalPath || !manifest.repoRelativePath) {
4733
+ return null;
4734
+ }
4735
+ const repoDir = path10.posix.dirname(manifest.repoRelativePath);
4736
+ const fileDir = path10.dirname(path10.resolve(manifest.originalPath));
4737
+ if (repoDir === "." || !repoDir) {
4738
+ return fileDir;
4739
+ }
4740
+ const segments = repoDir.split("/").filter(Boolean);
4741
+ return path10.resolve(fileDir, ...segments.map(() => ".."));
4742
+ }
4743
+ async function loadManifestArtifacts(paths) {
4744
+ const entries = await fs9.readdir(paths.manifestsDir, { withFileTypes: true }).catch(() => []);
4745
+ const manifests = await Promise.all(
4746
+ entries.filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map(async (entry) => await readJsonFile(path10.join(paths.manifestsDir, entry.name)))
4747
+ );
4748
+ return manifests.filter((manifest) => Boolean(manifest?.sourceId));
4749
+ }
4750
+ function buildLegacyDirectoryEntry(repoRoot, manifests) {
4751
+ const repoTitle = path10.basename(repoRoot) || repoRoot;
4752
+ const sourceIds = manifests.map((manifest) => manifest.sourceId).sort((left, right) => left.localeCompare(right));
4753
+ const createdAt = manifests.map((manifest) => manifest.createdAt).sort((left, right) => left.localeCompare(right))[0] ?? (/* @__PURE__ */ new Date()).toISOString();
4754
+ const updatedAt = manifests.map((manifest) => manifest.updatedAt).sort((left, right) => right.localeCompare(left))[0] ?? createdAt;
4755
+ return {
4756
+ id: `directory-${slugify(repoTitle)}-${sha256(repoRoot).slice(0, 8)}`,
4757
+ kind: "directory",
4758
+ title: repoTitle,
4759
+ path: repoRoot,
4760
+ repoRoot,
4761
+ createdAt,
4762
+ updatedAt,
4763
+ status: "ready",
4764
+ sourceIds,
4765
+ lastSyncAt: updatedAt,
4766
+ lastSyncStatus: "success",
4767
+ lastSyncCounts: {
4768
+ scannedCount: manifests.length,
4769
+ importedCount: manifests.length,
4770
+ updatedCount: 0,
4771
+ removedCount: 0,
4772
+ skippedCount: 0
4773
+ }
4774
+ };
4775
+ }
4776
+ async function buildLegacyArtifact(paths) {
4777
+ const manifests = await loadManifestArtifacts(paths);
4778
+ const manifestsByRepoRoot = /* @__PURE__ */ new Map();
4779
+ for (const manifest of manifests) {
4780
+ const repoRoot = repoRootFromManifest(manifest);
4781
+ if (!repoRoot) {
4782
+ continue;
4783
+ }
4784
+ const key = path10.resolve(repoRoot);
4785
+ const bucket = manifestsByRepoRoot.get(key) ?? [];
4786
+ bucket.push(manifest);
4787
+ manifestsByRepoRoot.set(key, bucket);
4788
+ }
4789
+ const repoRoots = [...manifestsByRepoRoot.entries()].filter(([, repoManifests]) => {
4790
+ return repoManifests.length > 1 || repoManifests.some((manifest) => manifest.sourceKind === "code");
4791
+ });
4792
+ return {
4793
+ version: MANAGED_SOURCES_VERSION,
4794
+ sources: repoRoots.sort((left, right) => left[0].localeCompare(right[0])).map(([repoRoot, repoManifests]) => buildLegacyDirectoryEntry(repoRoot, repoManifests))
4795
+ };
4796
+ }
4797
+ async function ensureManagedSourcesArtifact(rootDir) {
4798
+ const { paths } = await initWorkspace(rootDir);
4799
+ if (await fileExists(paths.managedSourcesPath)) {
4800
+ const existing = await readJsonFile(paths.managedSourcesPath);
4801
+ if (existing?.version === MANAGED_SOURCES_VERSION && Array.isArray(existing.sources)) {
4802
+ return {
4803
+ version: MANAGED_SOURCES_VERSION,
4804
+ sources: [...existing.sources].sort(
4805
+ (left, right) => left.createdAt.localeCompare(right.createdAt) || left.id.localeCompare(right.id)
4806
+ )
4807
+ };
4808
+ }
4809
+ }
4810
+ const artifact = await buildLegacyArtifact(paths);
4811
+ await writeJsonFile(paths.managedSourcesPath, artifact);
4812
+ return artifact;
4813
+ }
4814
+ async function loadManagedSources(rootDir) {
4815
+ const artifact = await ensureManagedSourcesArtifact(rootDir);
4816
+ return artifact.sources;
4817
+ }
4818
+ async function readManagedSourcesIfPresent(rootDir) {
4819
+ const { paths } = await initWorkspace(rootDir);
4820
+ if (!await fileExists(paths.managedSourcesPath)) {
4821
+ return null;
4822
+ }
4823
+ const existing = await readJsonFile(paths.managedSourcesPath);
4824
+ if (!existing?.version || !Array.isArray(existing.sources)) {
4825
+ return null;
4826
+ }
4827
+ return existing.sources;
4828
+ }
4829
+ async function saveManagedSources(rootDir, sources) {
4830
+ const { paths } = await initWorkspace(rootDir);
4831
+ await writeJsonFile(paths.managedSourcesPath, {
4832
+ version: MANAGED_SOURCES_VERSION,
4833
+ sources: [...sources].sort((left, right) => left.createdAt.localeCompare(right.createdAt) || left.id.localeCompare(right.id))
4834
+ });
4835
+ }
4836
+ async function managedSourceWorkingDir(rootDir, sourceId) {
4837
+ const { paths } = await initWorkspace(rootDir);
4838
+ return path10.join(paths.managedSourcesDir, sourceId);
4839
+ }
4840
+
4841
+ // src/watch-state.ts
4842
+ import fs10 from "fs/promises";
4843
+ import path11 from "path";
4730
4844
  import matter2 from "gray-matter";
4731
4845
  function pendingEntryKey(entry) {
4732
4846
  return entry.path;
@@ -4740,7 +4854,7 @@ function normalizeRelativePath(rootDir, filePath) {
4740
4854
  if (!filePath) {
4741
4855
  return void 0;
4742
4856
  }
4743
- return toPosix(path10.relative(rootDir, path10.resolve(filePath)));
4857
+ return toPosix(path11.relative(rootDir, path11.resolve(filePath)));
4744
4858
  }
4745
4859
  async function readPendingSemanticRefresh(rootDir) {
4746
4860
  const { paths } = await initWorkspace(rootDir);
@@ -4834,11 +4948,11 @@ async function markPagesStaleForSources(rootDir, sourceIds) {
4834
4948
  if (page.freshness !== "stale" || !page.sourceIds.some((sourceId) => affectedSourceIds.has(sourceId))) {
4835
4949
  continue;
4836
4950
  }
4837
- const absolutePath = path10.join(paths.wikiDir, page.path);
4951
+ const absolutePath = path11.join(paths.wikiDir, page.path);
4838
4952
  if (!await fileExists(absolutePath)) {
4839
4953
  continue;
4840
4954
  }
4841
- const raw = await fs9.readFile(absolutePath, "utf8");
4955
+ const raw = await fs10.readFile(absolutePath, "utf8");
4842
4956
  const parsed = matter2(raw);
4843
4957
  if (parsed.data.freshness === "stale") {
4844
4958
  continue;
@@ -4888,7 +5002,7 @@ function inferKind(mimeType, filePath) {
4888
5002
  return "binary";
4889
5003
  }
4890
5004
  function isRstFilePath(filePath) {
4891
- const extension = path11.extname(filePath).toLowerCase();
5005
+ const extension = path12.extname(filePath).toLowerCase();
4892
5006
  return extension === ".rst" || extension === ".rest";
4893
5007
  }
4894
5008
  function titleFromText(fallback, content, filePath) {
@@ -5031,7 +5145,7 @@ function normalizeIngestOptions(options) {
5031
5145
  return {
5032
5146
  includeAssets: options?.includeAssets ?? true,
5033
5147
  maxAssetSize: Math.max(0, Math.floor(options?.maxAssetSize ?? DEFAULT_MAX_ASSET_SIZE)),
5034
- repoRoot: options?.repoRoot ? path11.resolve(options.repoRoot) : void 0,
5148
+ repoRoot: options?.repoRoot ? path12.resolve(options.repoRoot) : void 0,
5035
5149
  include: (options?.include ?? []).map((pattern) => pattern.trim()).filter(Boolean),
5036
5150
  exclude: (options?.exclude ?? []).map((pattern) => pattern.trim()).filter(Boolean),
5037
5151
  maxFiles: Math.max(1, Math.floor(options?.maxFiles ?? DEFAULT_MAX_DIRECTORY_FILES)),
@@ -5051,27 +5165,27 @@ async function resolveRepoIngestOptions(rootDir, options) {
5051
5165
  }
5052
5166
  function matchesAnyGlob2(relativePath, patterns) {
5053
5167
  return patterns.some(
5054
- (pattern) => path11.matchesGlob(relativePath, pattern) || path11.matchesGlob(path11.posix.basename(relativePath), pattern)
5168
+ (pattern) => path12.matchesGlob(relativePath, pattern) || path12.matchesGlob(path12.posix.basename(relativePath), pattern)
5055
5169
  );
5056
5170
  }
5057
5171
  function supportedDirectoryKind(sourceKind) {
5058
5172
  return sourceKind !== "binary";
5059
5173
  }
5060
5174
  async function findNearestGitRoot2(startPath) {
5061
- let current = path11.resolve(startPath);
5175
+ let current = path12.resolve(startPath);
5062
5176
  try {
5063
- const stat = await fs10.stat(current);
5177
+ const stat = await fs11.stat(current);
5064
5178
  if (!stat.isDirectory()) {
5065
- current = path11.dirname(current);
5179
+ current = path12.dirname(current);
5066
5180
  }
5067
5181
  } catch {
5068
- current = path11.dirname(current);
5182
+ current = path12.dirname(current);
5069
5183
  }
5070
5184
  while (true) {
5071
- if (await fileExists(path11.join(current, ".git"))) {
5185
+ if (await fileExists(path12.join(current, ".git"))) {
5072
5186
  return current;
5073
5187
  }
5074
- const parent = path11.dirname(current);
5188
+ const parent = path12.dirname(current);
5075
5189
  if (parent === current) {
5076
5190
  return null;
5077
5191
  }
@@ -5079,26 +5193,26 @@ async function findNearestGitRoot2(startPath) {
5079
5193
  }
5080
5194
  }
5081
5195
  function withinRoot(rootPath, targetPath) {
5082
- const relative = path11.relative(rootPath, targetPath);
5083
- return relative === "" || !relative.startsWith("..") && !path11.isAbsolute(relative);
5196
+ const relative = path12.relative(rootPath, targetPath);
5197
+ return relative === "" || !relative.startsWith("..") && !path12.isAbsolute(relative);
5084
5198
  }
5085
- function repoRootFromManifest(manifest) {
5199
+ function repoRootFromManifest2(manifest) {
5086
5200
  if (manifest.originType !== "file" || !manifest.originalPath || !manifest.repoRelativePath) {
5087
5201
  return null;
5088
5202
  }
5089
- const repoDir = path11.posix.dirname(manifest.repoRelativePath);
5090
- const fileDir = path11.dirname(path11.resolve(manifest.originalPath));
5203
+ const repoDir = path12.posix.dirname(manifest.repoRelativePath);
5204
+ const fileDir = path12.dirname(path12.resolve(manifest.originalPath));
5091
5205
  if (repoDir === "." || !repoDir) {
5092
5206
  return fileDir;
5093
5207
  }
5094
5208
  const segments = repoDir.split("/").filter(Boolean);
5095
- return path11.resolve(fileDir, ...segments.map(() => ".."));
5209
+ return path12.resolve(fileDir, ...segments.map(() => ".."));
5096
5210
  }
5097
5211
  function repoRelativePathFor(absolutePath, repoRoot) {
5098
5212
  if (!repoRoot || !withinRoot(repoRoot, absolutePath)) {
5099
5213
  return void 0;
5100
5214
  }
5101
- const relative = toPosix(path11.relative(repoRoot, absolutePath));
5215
+ const relative = toPosix(path12.relative(repoRoot, absolutePath));
5102
5216
  return relative && !relative.startsWith("..") ? relative : void 0;
5103
5217
  }
5104
5218
  function normalizeOriginUrl(input) {
@@ -5430,7 +5544,7 @@ function buildCompositeHash(payloadBytes, attachments = []) {
5430
5544
  return sha256(`${sha256(payloadBytes)}|${attachmentSignature}`);
5431
5545
  }
5432
5546
  function sanitizeAssetRelativePath(value) {
5433
- const normalized = path11.posix.normalize(value.replace(/\\/g, "/"));
5547
+ const normalized = path12.posix.normalize(value.replace(/\\/g, "/"));
5434
5548
  const segments = normalized.split("/").filter(Boolean).map((segment) => {
5435
5549
  if (segment === ".") {
5436
5550
  return "";
@@ -5450,7 +5564,7 @@ function normalizeLocalReference(value) {
5450
5564
  return null;
5451
5565
  }
5452
5566
  const lowered = candidate.toLowerCase();
5453
- if (lowered.startsWith("http://") || lowered.startsWith("https://") || lowered.startsWith("data:") || lowered.startsWith("mailto:") || lowered.startsWith("#") || path11.isAbsolute(candidate)) {
5567
+ if (lowered.startsWith("http://") || lowered.startsWith("https://") || lowered.startsWith("data:") || lowered.startsWith("mailto:") || lowered.startsWith("#") || path12.isAbsolute(candidate)) {
5454
5568
  return null;
5455
5569
  }
5456
5570
  return candidate.replace(/\\/g, "/");
@@ -5528,12 +5642,12 @@ async function convertHtmlToMarkdown(html, url) {
5528
5642
  };
5529
5643
  }
5530
5644
  async function readManifestByHash(manifestsDir, contentHash) {
5531
- const entries = await fs10.readdir(manifestsDir, { withFileTypes: true }).catch(() => []);
5645
+ const entries = await fs11.readdir(manifestsDir, { withFileTypes: true }).catch(() => []);
5532
5646
  for (const entry of entries) {
5533
5647
  if (!entry.isFile() || !entry.name.endsWith(".json")) {
5534
5648
  continue;
5535
5649
  }
5536
- const manifest = await readJsonFile(path11.join(manifestsDir, entry.name));
5650
+ const manifest = await readJsonFile(path12.join(manifestsDir, entry.name));
5537
5651
  if (manifest?.contentHash === contentHash) {
5538
5652
  return manifest;
5539
5653
  }
@@ -5541,12 +5655,12 @@ async function readManifestByHash(manifestsDir, contentHash) {
5541
5655
  return null;
5542
5656
  }
5543
5657
  async function readManifestByOrigin(manifestsDir, prepared) {
5544
- const entries = await fs10.readdir(manifestsDir, { withFileTypes: true }).catch(() => []);
5658
+ const entries = await fs11.readdir(manifestsDir, { withFileTypes: true }).catch(() => []);
5545
5659
  for (const entry of entries) {
5546
5660
  if (!entry.isFile() || !entry.name.endsWith(".json")) {
5547
5661
  continue;
5548
5662
  }
5549
- const manifest = await readJsonFile(path11.join(manifestsDir, entry.name));
5663
+ const manifest = await readJsonFile(path12.join(manifestsDir, entry.name));
5550
5664
  if (manifest && manifestMatchesOrigin(manifest, prepared)) {
5551
5665
  return manifest;
5552
5666
  }
@@ -5557,12 +5671,12 @@ async function loadGitignoreMatcher(repoRoot, enabled) {
5557
5671
  if (!enabled) {
5558
5672
  return null;
5559
5673
  }
5560
- const gitignorePath = path11.join(repoRoot, ".gitignore");
5674
+ const gitignorePath = path12.join(repoRoot, ".gitignore");
5561
5675
  if (!await fileExists(gitignorePath)) {
5562
5676
  return null;
5563
5677
  }
5564
5678
  const matcher = ignore();
5565
- matcher.add(await fs10.readFile(gitignorePath, "utf8"));
5679
+ matcher.add(await fs11.readFile(gitignorePath, "utf8"));
5566
5680
  return matcher;
5567
5681
  }
5568
5682
  function builtInIgnoreReason(relativePath) {
@@ -5586,23 +5700,23 @@ async function collectDirectoryFiles(rootDir, inputDir, repoRoot, options) {
5586
5700
  if (!currentDir) {
5587
5701
  continue;
5588
5702
  }
5589
- const entries = await fs10.readdir(currentDir, { withFileTypes: true });
5703
+ const entries = await fs11.readdir(currentDir, { withFileTypes: true });
5590
5704
  entries.sort((left, right) => left.name.localeCompare(right.name));
5591
5705
  for (const entry of entries) {
5592
- const absolutePath = path11.join(currentDir, entry.name);
5593
- const relativeToRepo = repoRelativePathFor(absolutePath, repoRoot) ?? toPosix(path11.relative(inputDir, absolutePath));
5706
+ const absolutePath = path12.join(currentDir, entry.name);
5707
+ const relativeToRepo = repoRelativePathFor(absolutePath, repoRoot) ?? toPosix(path12.relative(inputDir, absolutePath));
5594
5708
  const relativePath = relativeToRepo || entry.name;
5595
5709
  const builtInReason = builtInIgnoreReason(relativePath);
5596
5710
  if (builtInReason) {
5597
- skipped.push({ path: toPosix(path11.relative(rootDir, absolutePath)), reason: builtInReason });
5711
+ skipped.push({ path: toPosix(path12.relative(rootDir, absolutePath)), reason: builtInReason });
5598
5712
  continue;
5599
5713
  }
5600
5714
  if (matcher?.ignores(relativePath)) {
5601
- skipped.push({ path: toPosix(path11.relative(rootDir, absolutePath)), reason: "gitignore" });
5715
+ skipped.push({ path: toPosix(path12.relative(rootDir, absolutePath)), reason: "gitignore" });
5602
5716
  continue;
5603
5717
  }
5604
5718
  if (matchesAnyGlob2(relativePath, options.exclude)) {
5605
- skipped.push({ path: toPosix(path11.relative(rootDir, absolutePath)), reason: "exclude_glob" });
5719
+ skipped.push({ path: toPosix(path12.relative(rootDir, absolutePath)), reason: "exclude_glob" });
5606
5720
  continue;
5607
5721
  }
5608
5722
  if (entry.isDirectory()) {
@@ -5610,26 +5724,26 @@ async function collectDirectoryFiles(rootDir, inputDir, repoRoot, options) {
5610
5724
  continue;
5611
5725
  }
5612
5726
  if (!entry.isFile()) {
5613
- skipped.push({ path: toPosix(path11.relative(rootDir, absolutePath)), reason: "unsupported_entry" });
5727
+ skipped.push({ path: toPosix(path12.relative(rootDir, absolutePath)), reason: "unsupported_entry" });
5614
5728
  continue;
5615
5729
  }
5616
5730
  if (options.include.length > 0 && !matchesAnyGlob2(relativePath, options.include)) {
5617
- skipped.push({ path: toPosix(path11.relative(rootDir, absolutePath)), reason: "include_glob" });
5731
+ skipped.push({ path: toPosix(path12.relative(rootDir, absolutePath)), reason: "include_glob" });
5618
5732
  continue;
5619
5733
  }
5620
5734
  const mimeType = guessMimeType(absolutePath);
5621
5735
  const sourceKind = inferKind(mimeType, absolutePath);
5622
5736
  const sourceClass = sourceClassForRelativePath(relativePath, options);
5623
5737
  if (!supportedDirectoryKind(sourceKind)) {
5624
- skipped.push({ path: toPosix(path11.relative(rootDir, absolutePath)), reason: `unsupported_kind:${sourceKind}` });
5738
+ skipped.push({ path: toPosix(path12.relative(rootDir, absolutePath)), reason: `unsupported_kind:${sourceKind}` });
5625
5739
  continue;
5626
5740
  }
5627
5741
  if (!options.extractClasses.includes(sourceClass)) {
5628
- skipped.push({ path: toPosix(path11.relative(rootDir, absolutePath)), reason: `source_class:${sourceClass}` });
5742
+ skipped.push({ path: toPosix(path12.relative(rootDir, absolutePath)), reason: `source_class:${sourceClass}` });
5629
5743
  continue;
5630
5744
  }
5631
5745
  if (files.length >= options.maxFiles) {
5632
- skipped.push({ path: toPosix(path11.relative(rootDir, absolutePath)), reason: "max_files" });
5746
+ skipped.push({ path: toPosix(path12.relative(rootDir, absolutePath)), reason: "max_files" });
5633
5747
  continue;
5634
5748
  }
5635
5749
  files.push(absolutePath);
@@ -5651,12 +5765,12 @@ function resolveUrlMimeType(input, response) {
5651
5765
  function buildRemoteAssetRelativePath(assetUrl, mimeType) {
5652
5766
  const url = new URL(assetUrl);
5653
5767
  const normalized = sanitizeAssetRelativePath(`${url.hostname}${url.pathname || "/asset"}`);
5654
- const extension = path11.posix.extname(normalized);
5655
- const directory = path11.posix.dirname(normalized);
5656
- const basename = extension ? path11.posix.basename(normalized, extension) : path11.posix.basename(normalized);
5768
+ const extension = path12.posix.extname(normalized);
5769
+ const directory = path12.posix.dirname(normalized);
5770
+ const basename = extension ? path12.posix.basename(normalized, extension) : path12.posix.basename(normalized);
5657
5771
  const resolvedExtension = extension || `.${mime.extension(mimeType) || "bin"}`;
5658
5772
  const hashedName = `${basename || "asset"}-${sha256(assetUrl).slice(0, 8)}${resolvedExtension}`;
5659
- return directory === "." ? hashedName : path11.posix.join(directory, hashedName);
5773
+ return directory === "." ? hashedName : path12.posix.join(directory, hashedName);
5660
5774
  }
5661
5775
  async function readResponseBytesWithinLimit(response, maxBytes) {
5662
5776
  const contentLength = Number.parseInt(response.headers.get("content-length") ?? "", 10);
@@ -5809,34 +5923,34 @@ async function persistPreparedInput(rootDir, prepared, paths) {
5809
5923
  const previous = existingByOrigin ?? void 0;
5810
5924
  const sourceId = previous?.sourceId ?? `${slugify(prepared.title)}-${contentHash.slice(0, 8)}`;
5811
5925
  const now = (/* @__PURE__ */ new Date()).toISOString();
5812
- const storedPath = path11.join(paths.rawSourcesDir, `${sourceId}${prepared.storedExtension}`);
5813
- const extractedTextPath = prepared.extractedText ? path11.join(paths.extractsDir, `${sourceId}.md`) : void 0;
5814
- const extractedMetadataPath = prepared.extractionArtifact ? path11.join(paths.extractsDir, `${sourceId}.json`) : void 0;
5815
- const attachmentsDir = path11.join(paths.rawAssetsDir, sourceId);
5926
+ const storedPath = path12.join(paths.rawSourcesDir, `${sourceId}${prepared.storedExtension}`);
5927
+ const extractedTextPath = prepared.extractedText ? path12.join(paths.extractsDir, `${sourceId}.md`) : void 0;
5928
+ const extractedMetadataPath = prepared.extractionArtifact ? path12.join(paths.extractsDir, `${sourceId}.json`) : void 0;
5929
+ const attachmentsDir = path12.join(paths.rawAssetsDir, sourceId);
5816
5930
  if (previous?.storedPath) {
5817
- await fs10.rm(path11.resolve(rootDir, previous.storedPath), { force: true });
5931
+ await fs11.rm(path12.resolve(rootDir, previous.storedPath), { force: true });
5818
5932
  }
5819
5933
  if (previous?.extractedTextPath) {
5820
- await fs10.rm(path11.resolve(rootDir, previous.extractedTextPath), { force: true });
5934
+ await fs11.rm(path12.resolve(rootDir, previous.extractedTextPath), { force: true });
5821
5935
  }
5822
5936
  if (previous?.extractedMetadataPath) {
5823
- await fs10.rm(path11.resolve(rootDir, previous.extractedMetadataPath), { force: true });
5937
+ await fs11.rm(path12.resolve(rootDir, previous.extractedMetadataPath), { force: true });
5824
5938
  }
5825
- await fs10.rm(attachmentsDir, { recursive: true, force: true });
5826
- await fs10.writeFile(storedPath, prepared.payloadBytes);
5939
+ await fs11.rm(attachmentsDir, { recursive: true, force: true });
5940
+ await fs11.writeFile(storedPath, prepared.payloadBytes);
5827
5941
  if (prepared.extractedText && extractedTextPath) {
5828
- await fs10.writeFile(extractedTextPath, prepared.extractedText, "utf8");
5942
+ await fs11.writeFile(extractedTextPath, prepared.extractedText, "utf8");
5829
5943
  }
5830
5944
  if (prepared.extractionArtifact && extractedMetadataPath) {
5831
5945
  await writeJsonFile(extractedMetadataPath, prepared.extractionArtifact);
5832
5946
  }
5833
5947
  const manifestAttachments = [];
5834
5948
  for (const attachment of attachments) {
5835
- const absoluteAttachmentPath = path11.join(attachmentsDir, attachment.relativePath);
5836
- await ensureDir(path11.dirname(absoluteAttachmentPath));
5837
- await fs10.writeFile(absoluteAttachmentPath, attachment.bytes);
5949
+ const absoluteAttachmentPath = path12.join(attachmentsDir, attachment.relativePath);
5950
+ await ensureDir(path12.dirname(absoluteAttachmentPath));
5951
+ await fs11.writeFile(absoluteAttachmentPath, attachment.bytes);
5838
5952
  manifestAttachments.push({
5839
- path: toPosix(path11.relative(rootDir, absoluteAttachmentPath)),
5953
+ path: toPosix(path12.relative(rootDir, absoluteAttachmentPath)),
5840
5954
  mimeType: attachment.mimeType,
5841
5955
  originalPath: attachment.originalPath
5842
5956
  });
@@ -5852,9 +5966,9 @@ async function persistPreparedInput(rootDir, prepared, paths) {
5852
5966
  originalPath: prepared.originalPath,
5853
5967
  repoRelativePath: prepared.repoRelativePath,
5854
5968
  url: prepared.url,
5855
- storedPath: toPosix(path11.relative(rootDir, storedPath)),
5856
- extractedTextPath: extractedTextPath ? toPosix(path11.relative(rootDir, extractedTextPath)) : void 0,
5857
- extractedMetadataPath: extractedMetadataPath ? toPosix(path11.relative(rootDir, extractedMetadataPath)) : void 0,
5969
+ storedPath: toPosix(path12.relative(rootDir, storedPath)),
5970
+ extractedTextPath: extractedTextPath ? toPosix(path12.relative(rootDir, extractedTextPath)) : void 0,
5971
+ extractedMetadataPath: extractedMetadataPath ? toPosix(path12.relative(rootDir, extractedMetadataPath)) : void 0,
5858
5972
  extractionHash,
5859
5973
  mimeType: prepared.mimeType,
5860
5974
  contentHash,
@@ -5862,7 +5976,7 @@ async function persistPreparedInput(rootDir, prepared, paths) {
5862
5976
  updatedAt: now,
5863
5977
  attachments: manifestAttachments.length ? manifestAttachments : void 0
5864
5978
  };
5865
- await writeJsonFile(path11.join(paths.manifestsDir, `${sourceId}.json`), manifest);
5979
+ await writeJsonFile(path12.join(paths.manifestsDir, `${sourceId}.json`), manifest);
5866
5980
  await appendLogEntry(rootDir, "ingest", prepared.title, [
5867
5981
  `source_id=${sourceId}`,
5868
5982
  `kind=${prepared.sourceKind}`,
@@ -5880,16 +5994,16 @@ async function persistPreparedInput(rootDir, prepared, paths) {
5880
5994
  return { manifest, isNew: !previous, wasUpdated: Boolean(previous) };
5881
5995
  }
5882
5996
  async function removeManifestArtifacts(rootDir, manifest, paths) {
5883
- await fs10.rm(path11.join(paths.manifestsDir, `${manifest.sourceId}.json`), { force: true });
5884
- await fs10.rm(path11.resolve(rootDir, manifest.storedPath), { force: true });
5997
+ await fs11.rm(path12.join(paths.manifestsDir, `${manifest.sourceId}.json`), { force: true });
5998
+ await fs11.rm(path12.resolve(rootDir, manifest.storedPath), { force: true });
5885
5999
  if (manifest.extractedTextPath) {
5886
- await fs10.rm(path11.resolve(rootDir, manifest.extractedTextPath), { force: true });
6000
+ await fs11.rm(path12.resolve(rootDir, manifest.extractedTextPath), { force: true });
5887
6001
  }
5888
6002
  if (manifest.extractedMetadataPath) {
5889
- await fs10.rm(path11.resolve(rootDir, manifest.extractedMetadataPath), { force: true });
6003
+ await fs11.rm(path12.resolve(rootDir, manifest.extractedMetadataPath), { force: true });
5890
6004
  }
5891
- await fs10.rm(path11.join(paths.rawAssetsDir, manifest.sourceId), { recursive: true, force: true });
5892
- await fs10.rm(path11.join(paths.analysesDir, `${manifest.sourceId}.json`), { force: true });
6005
+ await fs11.rm(path12.join(paths.rawAssetsDir, manifest.sourceId), { recursive: true, force: true });
6006
+ await fs11.rm(path12.join(paths.analysesDir, `${manifest.sourceId}.json`), { force: true });
5893
6007
  }
5894
6008
  function repoSyncWorkspaceIgnorePaths(rootDir, paths, repoRoot) {
5895
6009
  const candidates = [
@@ -5898,11 +6012,11 @@ function repoSyncWorkspaceIgnorePaths(rootDir, paths, repoRoot) {
5898
6012
  paths.stateDir,
5899
6013
  paths.agentDir,
5900
6014
  paths.inboxDir,
5901
- path11.join(rootDir, ".claude"),
5902
- path11.join(rootDir, ".cursor"),
5903
- path11.join(rootDir, ".obsidian")
6015
+ path12.join(rootDir, ".claude"),
6016
+ path12.join(rootDir, ".cursor"),
6017
+ path12.join(rootDir, ".obsidian")
5904
6018
  ];
5905
- return candidates.map((candidate) => path11.resolve(candidate)).filter((candidate, index, items) => items.indexOf(candidate) === index).filter((candidate) => withinRoot(repoRoot, candidate));
6019
+ return candidates.map((candidate) => path12.resolve(candidate)).filter((candidate, index, items) => items.indexOf(candidate) === index).filter((candidate) => withinRoot(repoRoot, candidate));
5906
6020
  }
5907
6021
  function preparedMatchesManifest(manifest, prepared, contentHash) {
5908
6022
  return manifest.contentHash === contentHash && manifest.extractionHash === (prepared.extractionHash ?? buildExtractionHash(prepared.extractedText, prepared.extractionArtifact)) && manifest.title === prepared.title && manifest.sourceKind === prepared.sourceKind && manifest.sourceType === prepared.sourceType && manifest.sourceClass === prepared.sourceClass && manifest.language === prepared.language && manifest.mimeType === prepared.mimeType && manifest.repoRelativePath === prepared.repoRelativePath;
@@ -5914,8 +6028,15 @@ function pendingSemanticRefreshId(changeType, repoRoot, relativePath) {
5914
6028
  return `pending:${changeType}:${sha256(`${toPosix(repoRoot)}:${relativePath}`).slice(0, 12)}`;
5915
6029
  }
5916
6030
  async function listTrackedRepoRoots(rootDir) {
6031
+ const managedSources = await readManagedSourcesIfPresent(rootDir).catch(() => null);
6032
+ if (managedSources && managedSources.length > 0) {
6033
+ const directoryRoots = managedSources.filter((source) => source.kind === "directory" && source.path).map((source) => path12.resolve(source.path));
6034
+ if (directoryRoots.length > 0) {
6035
+ return [...new Set(directoryRoots)].sort((left, right) => left.localeCompare(right));
6036
+ }
6037
+ }
5917
6038
  const manifests = await listManifests(rootDir);
5918
- return [...new Set(manifests.map((manifest) => repoRootFromManifest(manifest)).filter((item) => Boolean(item)))].sort(
6039
+ return [...new Set(manifests.map((manifest) => repoRootFromManifest2(manifest)).filter((item) => Boolean(item)))].sort(
5919
6040
  (left, right) => left.localeCompare(right)
5920
6041
  );
5921
6042
  }
@@ -5924,16 +6045,16 @@ async function syncTrackedRepos(rootDir, options, repoRoots) {
5924
6045
  const normalizedOptions = await resolveRepoIngestOptions(rootDir, options);
5925
6046
  const manifests = await listManifests(rootDir);
5926
6047
  const trackedRoots = (repoRoots && repoRoots.length > 0 ? repoRoots : await listTrackedRepoRoots(rootDir)).map(
5927
- (item) => path11.resolve(item)
6048
+ (item) => path12.resolve(item)
5928
6049
  );
5929
6050
  const uniqueRoots = [...new Set(trackedRoots)].sort((left, right) => left.localeCompare(right));
5930
6051
  const manifestsByRepoRoot = /* @__PURE__ */ new Map();
5931
6052
  for (const manifest of manifests) {
5932
- const repoRoot = repoRootFromManifest(manifest);
5933
- if (!repoRoot || !uniqueRoots.includes(path11.resolve(repoRoot))) {
6053
+ const repoRoot = repoRootFromManifest2(manifest);
6054
+ if (!repoRoot || !uniqueRoots.includes(path12.resolve(repoRoot))) {
5934
6055
  continue;
5935
6056
  }
5936
- const key = path11.resolve(repoRoot);
6057
+ const key = path12.resolve(repoRoot);
5937
6058
  const bucket = manifestsByRepoRoot.get(key) ?? [];
5938
6059
  bucket.push(manifest);
5939
6060
  manifestsByRepoRoot.set(key, bucket);
@@ -5958,15 +6079,15 @@ async function syncTrackedRepos(rootDir, options, repoRoots) {
5958
6079
  skipped.push(
5959
6080
  ...collected.skipped,
5960
6081
  ...collected.files.filter((absolutePath) => ignoreRoots.some((ignoreRoot) => withinRoot(ignoreRoot, absolutePath))).map((absolutePath) => ({
5961
- path: toPosix(path11.relative(rootDir, absolutePath)),
6082
+ path: toPosix(path12.relative(rootDir, absolutePath)),
5962
6083
  reason: "workspace_generated"
5963
6084
  }))
5964
6085
  );
5965
6086
  scannedCount += files.length;
5966
6087
  const progress = createProgressReporter("sync", files.length);
5967
- const currentPaths = new Set(files.map((absolutePath) => path11.resolve(absolutePath)));
6088
+ const currentPaths = new Set(files.map((absolutePath) => path12.resolve(absolutePath)));
5968
6089
  for (const absolutePath of files) {
5969
- const relativePath = repoRelativePathFor(absolutePath, repoRoot) ?? toPosix(path11.relative(repoRoot, absolutePath));
6090
+ const relativePath = repoRelativePathFor(absolutePath, repoRoot) ?? toPosix(path12.relative(repoRoot, absolutePath));
5970
6091
  const prepared = await prepareFileInput(rootDir, absolutePath, repoRoot, sourceClassForRelativePath(relativePath, normalizedOptions));
5971
6092
  const result = await persistPreparedInput(rootDir, prepared, paths);
5972
6093
  if (result.isNew) {
@@ -5976,9 +6097,9 @@ async function syncTrackedRepos(rootDir, options, repoRoots) {
5976
6097
  }
5977
6098
  progress.tick();
5978
6099
  }
5979
- progress.finish(`repo=${toPosix(path11.relative(rootDir, repoRoot)) || "."}`);
6100
+ progress.finish(`repo=${toPosix(path12.relative(rootDir, repoRoot)) || "."}`);
5980
6101
  for (const manifest of repoManifests) {
5981
- const originalPath = manifest.originalPath ? path11.resolve(manifest.originalPath) : null;
6102
+ const originalPath = manifest.originalPath ? path12.resolve(manifest.originalPath) : null;
5982
6103
  if (originalPath && !currentPaths.has(originalPath)) {
5983
6104
  await removeManifestArtifacts(rootDir, manifest, paths);
5984
6105
  removed.push(manifest);
@@ -5986,7 +6107,7 @@ async function syncTrackedRepos(rootDir, options, repoRoots) {
5986
6107
  }
5987
6108
  }
5988
6109
  if (uniqueRoots.length > 0) {
5989
- await appendLogEntry(rootDir, "sync_repo", uniqueRoots.map((repoRoot) => toPosix(path11.relative(rootDir, repoRoot)) || ".").join(","), [
6110
+ await appendLogEntry(rootDir, "sync_repo", uniqueRoots.map((repoRoot) => toPosix(path12.relative(rootDir, repoRoot)) || ".").join(","), [
5990
6111
  `repo_roots=${uniqueRoots.length}`,
5991
6112
  `scanned=${scannedCount}`,
5992
6113
  `imported=${imported.length}`,
@@ -6009,16 +6130,16 @@ async function syncTrackedReposForWatch(rootDir, options, repoRoots) {
6009
6130
  const normalizedOptions = await resolveRepoIngestOptions(rootDir, options);
6010
6131
  const manifests = await listManifests(rootDir);
6011
6132
  const trackedRoots = (repoRoots && repoRoots.length > 0 ? repoRoots : await listTrackedRepoRoots(rootDir)).map(
6012
- (item) => path11.resolve(item)
6133
+ (item) => path12.resolve(item)
6013
6134
  );
6014
6135
  const uniqueRoots = [...new Set(trackedRoots)].sort((left, right) => left.localeCompare(right));
6015
6136
  const manifestsByRepoRoot = /* @__PURE__ */ new Map();
6016
6137
  for (const manifest of manifests) {
6017
- const repoRoot = repoRootFromManifest(manifest);
6018
- if (!repoRoot || !uniqueRoots.includes(path11.resolve(repoRoot))) {
6138
+ const repoRoot = repoRootFromManifest2(manifest);
6139
+ if (!repoRoot || !uniqueRoots.includes(path12.resolve(repoRoot))) {
6019
6140
  continue;
6020
6141
  }
6021
- const key = path11.resolve(repoRoot);
6142
+ const key = path12.resolve(repoRoot);
6022
6143
  const bucket = manifestsByRepoRoot.get(key) ?? [];
6023
6144
  bucket.push(manifest);
6024
6145
  manifestsByRepoRoot.set(key, bucket);
@@ -6033,7 +6154,7 @@ async function syncTrackedReposForWatch(rootDir, options, repoRoots) {
6033
6154
  for (const repoRoot of uniqueRoots) {
6034
6155
  const repoManifests = manifestsByRepoRoot.get(repoRoot) ?? [];
6035
6156
  const manifestsByOriginalPath = new Map(
6036
- repoManifests.filter((manifest) => manifest.originalPath).map((manifest) => [path11.resolve(manifest.originalPath), manifest])
6157
+ repoManifests.filter((manifest) => manifest.originalPath).map((manifest) => [path12.resolve(manifest.originalPath), manifest])
6037
6158
  );
6038
6159
  if (!await fileExists(repoRoot)) {
6039
6160
  for (const manifest of repoManifests) {
@@ -6041,7 +6162,7 @@ async function syncTrackedReposForWatch(rootDir, options, repoRoots) {
6041
6162
  pendingSemanticRefresh.push({
6042
6163
  id: pendingSemanticRefreshId("removed", repoRoot, manifest.repoRelativePath ?? manifest.storedPath),
6043
6164
  repoRoot,
6044
- path: toPosix(path11.relative(rootDir, manifest.originalPath ?? manifest.storedPath)),
6165
+ path: toPosix(path12.relative(rootDir, manifest.originalPath ?? manifest.storedPath)),
6045
6166
  changeType: "removed",
6046
6167
  detectedAt: (/* @__PURE__ */ new Date()).toISOString(),
6047
6168
  sourceId: manifest.sourceId,
@@ -6061,18 +6182,18 @@ async function syncTrackedReposForWatch(rootDir, options, repoRoots) {
6061
6182
  skipped.push(
6062
6183
  ...collected.skipped,
6063
6184
  ...collected.files.filter((absolutePath) => ignoreRoots.some((ignoreRoot) => withinRoot(ignoreRoot, absolutePath))).map((absolutePath) => ({
6064
- path: toPosix(path11.relative(rootDir, absolutePath)),
6185
+ path: toPosix(path12.relative(rootDir, absolutePath)),
6065
6186
  reason: "workspace_generated"
6066
6187
  }))
6067
6188
  );
6068
6189
  scannedCount += files.length;
6069
6190
  const progress = createProgressReporter("sync-watch", files.length);
6070
- const currentPaths = new Set(files.map((absolutePath) => path11.resolve(absolutePath)));
6191
+ const currentPaths = new Set(files.map((absolutePath) => path12.resolve(absolutePath)));
6071
6192
  for (const absolutePath of files) {
6072
- const relativePath = repoRelativePathFor(absolutePath, repoRoot) ?? toPosix(path11.relative(repoRoot, absolutePath));
6193
+ const relativePath = repoRelativePathFor(absolutePath, repoRoot) ?? toPosix(path12.relative(repoRoot, absolutePath));
6073
6194
  const prepared = await prepareFileInput(rootDir, absolutePath, repoRoot, sourceClassForRelativePath(relativePath, normalizedOptions));
6074
6195
  if (shouldDeferWatchSemanticRefresh(prepared.sourceKind)) {
6075
- const existing = manifestsByOriginalPath.get(path11.resolve(absolutePath));
6196
+ const existing = manifestsByOriginalPath.get(path12.resolve(absolutePath));
6076
6197
  const contentHash = buildCompositeHash(prepared.payloadBytes, prepared.attachments);
6077
6198
  const changed = !existing || !preparedMatchesManifest(existing, prepared, contentHash);
6078
6199
  if (changed) {
@@ -6080,10 +6201,10 @@ async function syncTrackedReposForWatch(rootDir, options, repoRoots) {
6080
6201
  id: pendingSemanticRefreshId(
6081
6202
  existing ? "modified" : "added",
6082
6203
  repoRoot,
6083
- prepared.repoRelativePath ?? toPosix(path11.relative(repoRoot, absolutePath))
6204
+ prepared.repoRelativePath ?? toPosix(path12.relative(repoRoot, absolutePath))
6084
6205
  ),
6085
6206
  repoRoot,
6086
- path: toPosix(path11.relative(rootDir, absolutePath)),
6207
+ path: toPosix(path12.relative(rootDir, absolutePath)),
6087
6208
  changeType: existing ? "modified" : "added",
6088
6209
  detectedAt: (/* @__PURE__ */ new Date()).toISOString(),
6089
6210
  sourceId: existing?.sourceId,
@@ -6104,15 +6225,15 @@ async function syncTrackedReposForWatch(rootDir, options, repoRoots) {
6104
6225
  }
6105
6226
  progress.tick();
6106
6227
  }
6107
- progress.finish(`repo=${toPosix(path11.relative(rootDir, repoRoot)) || "."}`);
6228
+ progress.finish(`repo=${toPosix(path12.relative(rootDir, repoRoot)) || "."}`);
6108
6229
  for (const manifest of repoManifests) {
6109
- const originalPath = manifest.originalPath ? path11.resolve(manifest.originalPath) : null;
6230
+ const originalPath = manifest.originalPath ? path12.resolve(manifest.originalPath) : null;
6110
6231
  if (originalPath && !currentPaths.has(originalPath)) {
6111
6232
  if (shouldDeferWatchSemanticRefresh(manifest.sourceKind)) {
6112
6233
  pendingSemanticRefresh.push({
6113
- id: pendingSemanticRefreshId("removed", repoRoot, manifest.repoRelativePath ?? toPosix(path11.relative(repoRoot, originalPath))),
6234
+ id: pendingSemanticRefreshId("removed", repoRoot, manifest.repoRelativePath ?? toPosix(path12.relative(repoRoot, originalPath))),
6114
6235
  repoRoot,
6115
- path: toPosix(path11.relative(rootDir, originalPath)),
6236
+ path: toPosix(path12.relative(rootDir, originalPath)),
6116
6237
  changeType: "removed",
6117
6238
  detectedAt: (/* @__PURE__ */ new Date()).toISOString(),
6118
6239
  sourceId: manifest.sourceId,
@@ -6130,7 +6251,7 @@ async function syncTrackedReposForWatch(rootDir, options, repoRoots) {
6130
6251
  await appendLogEntry(
6131
6252
  rootDir,
6132
6253
  "sync_repo_watch",
6133
- uniqueRoots.map((repoRoot) => toPosix(path11.relative(rootDir, repoRoot)) || ".").join(","),
6254
+ uniqueRoots.map((repoRoot) => toPosix(path12.relative(rootDir, repoRoot)) || ".").join(","),
6134
6255
  [
6135
6256
  `repo_roots=${uniqueRoots.length}`,
6136
6257
  `scanned=${scannedCount}`,
@@ -6156,17 +6277,17 @@ async function syncTrackedReposForWatch(rootDir, options, repoRoots) {
6156
6277
  };
6157
6278
  }
6158
6279
  async function prepareFileInput(rootDir, absoluteInput, repoRoot, sourceClass) {
6159
- const payloadBytes = await fs10.readFile(absoluteInput);
6280
+ const payloadBytes = await fs11.readFile(absoluteInput);
6160
6281
  const mimeType = guessMimeType(absoluteInput);
6161
6282
  const sourceKind = inferKind(mimeType, absoluteInput);
6162
6283
  const language = inferCodeLanguage(absoluteInput, mimeType);
6163
- const storedExtension = path11.extname(absoluteInput) || `.${mime.extension(mimeType) || "bin"}`;
6284
+ const storedExtension = path12.extname(absoluteInput) || `.${mime.extension(mimeType) || "bin"}`;
6164
6285
  let title;
6165
6286
  let extractedText;
6166
6287
  let extractionArtifact;
6167
6288
  if (sourceKind === "markdown" || sourceKind === "text" || sourceKind === "code") {
6168
6289
  extractedText = extractedTextForPlainSource(absoluteInput, sourceKind, payloadBytes.toString("utf8"));
6169
- title = titleFromText(path11.basename(absoluteInput, path11.extname(absoluteInput)), extractedText, absoluteInput);
6290
+ title = titleFromText(path12.basename(absoluteInput, path12.extname(absoluteInput)), extractedText, absoluteInput);
6170
6291
  extractionArtifact = createPlainTextExtractionArtifact(sourceKind, mimeType);
6171
6292
  } else if (sourceKind === "html") {
6172
6293
  const html = payloadBytes.toString("utf8");
@@ -6175,18 +6296,18 @@ async function prepareFileInput(rootDir, absoluteInput, repoRoot, sourceClass) {
6175
6296
  extractedText = converted.markdown;
6176
6297
  extractionArtifact = createHtmlReadabilityExtractionArtifact(sourceKind, mimeType);
6177
6298
  } else if (sourceKind === "pdf") {
6178
- title = path11.basename(absoluteInput, path11.extname(absoluteInput));
6299
+ title = path12.basename(absoluteInput, path12.extname(absoluteInput));
6179
6300
  const extracted = await extractPdfText({ mimeType, bytes: payloadBytes });
6180
6301
  extractedText = extracted.extractedText;
6181
6302
  extractionArtifact = extracted.artifact;
6182
6303
  } else if (sourceKind === "docx") {
6183
- title = path11.basename(absoluteInput, path11.extname(absoluteInput));
6304
+ title = path12.basename(absoluteInput, path12.extname(absoluteInput));
6184
6305
  const extracted = await extractDocxText({ mimeType, bytes: payloadBytes });
6185
6306
  title = extracted.artifact.metadata?.title?.trim() || title;
6186
6307
  extractedText = extracted.extractedText;
6187
6308
  extractionArtifact = extracted.artifact;
6188
6309
  } else if (sourceKind === "image") {
6189
- title = path11.basename(absoluteInput, path11.extname(absoluteInput));
6310
+ title = path12.basename(absoluteInput, path12.extname(absoluteInput));
6190
6311
  const extracted = await extractImageWithVision(rootDir, {
6191
6312
  title,
6192
6313
  mimeType,
@@ -6196,7 +6317,7 @@ async function prepareFileInput(rootDir, absoluteInput, repoRoot, sourceClass) {
6196
6317
  extractedText = extracted.extractedText;
6197
6318
  extractionArtifact = extracted.artifact;
6198
6319
  } else {
6199
- title = path11.basename(absoluteInput, path11.extname(absoluteInput));
6320
+ title = path12.basename(absoluteInput, path12.extname(absoluteInput));
6200
6321
  }
6201
6322
  return {
6202
6323
  title,
@@ -6273,7 +6394,7 @@ async function prepareUrlInput(rootDir, input, options) {
6273
6394
  sourceKind = "markdown";
6274
6395
  storedExtension = ".md";
6275
6396
  } else {
6276
- const extension = path11.extname(inputUrl.pathname);
6397
+ const extension = path12.extname(inputUrl.pathname);
6277
6398
  storedExtension = extension || `.${mime.extension(mimeType) || "bin"}`;
6278
6399
  if (sourceKind === "markdown" || sourceKind === "text" || sourceKind === "code") {
6279
6400
  extractedText = extractedTextForPlainSource(inputUrl.pathname, sourceKind, payloadBytes.toString("utf8"));
@@ -6344,14 +6465,14 @@ async function collectInboxAttachmentRefs(inputDir, files) {
6344
6465
  if (sourceKind !== "markdown" && sourceKind !== "html") {
6345
6466
  continue;
6346
6467
  }
6347
- const content = await fs10.readFile(absolutePath, "utf8");
6468
+ const content = await fs11.readFile(absolutePath, "utf8");
6348
6469
  const refs = sourceKind === "html" ? extractHtmlLocalReferences(content, pathToFileURL(absolutePath).toString()) : extractMarkdownReferences(content);
6349
6470
  if (!refs.length) {
6350
6471
  continue;
6351
6472
  }
6352
6473
  const sourceRefs = [];
6353
6474
  for (const ref of refs) {
6354
- const resolved = path11.resolve(path11.dirname(absolutePath), ref);
6475
+ const resolved = path12.resolve(path12.dirname(absolutePath), ref);
6355
6476
  if (!resolved.startsWith(inputDir) || !await fileExists(resolved)) {
6356
6477
  continue;
6357
6478
  }
@@ -6385,12 +6506,12 @@ function rewriteMarkdownReferences(content, replacements) {
6385
6506
  });
6386
6507
  }
6387
6508
  async function prepareInboxMarkdownInput(absolutePath, attachmentRefs) {
6388
- const originalBytes = await fs10.readFile(absolutePath);
6509
+ const originalBytes = await fs11.readFile(absolutePath);
6389
6510
  const originalText = originalBytes.toString("utf8");
6390
- const title = titleFromText(path11.basename(absolutePath, path11.extname(absolutePath)), originalText);
6511
+ const title = titleFromText(path12.basename(absolutePath, path12.extname(absolutePath)), originalText);
6391
6512
  const attachments = [];
6392
6513
  for (const attachmentRef of attachmentRefs) {
6393
- const bytes = await fs10.readFile(attachmentRef.absolutePath);
6514
+ const bytes = await fs11.readFile(attachmentRef.absolutePath);
6394
6515
  attachments.push({
6395
6516
  relativePath: sanitizeAssetRelativePath(attachmentRef.relativeRef),
6396
6517
  mimeType: guessMimeType(attachmentRef.absolutePath),
@@ -6414,7 +6535,7 @@ async function prepareInboxMarkdownInput(absolutePath, attachmentRefs) {
6414
6535
  sourceKind: "markdown",
6415
6536
  originalPath: toPosix(absolutePath),
6416
6537
  mimeType: "text/markdown",
6417
- storedExtension: path11.extname(absolutePath) || ".md",
6538
+ storedExtension: path12.extname(absolutePath) || ".md",
6418
6539
  payloadBytes: Buffer.from(rewrittenText, "utf8"),
6419
6540
  extractedText: rewrittenText,
6420
6541
  extractionArtifact,
@@ -6424,12 +6545,12 @@ async function prepareInboxMarkdownInput(absolutePath, attachmentRefs) {
6424
6545
  };
6425
6546
  }
6426
6547
  async function prepareInboxHtmlInput(absolutePath, attachmentRefs) {
6427
- const originalBytes = await fs10.readFile(absolutePath);
6548
+ const originalBytes = await fs11.readFile(absolutePath);
6428
6549
  const originalHtml = originalBytes.toString("utf8");
6429
6550
  const initialConversion = await convertHtmlToMarkdown(originalHtml, pathToFileURL(absolutePath).toString());
6430
6551
  const attachments = [];
6431
6552
  for (const attachmentRef of attachmentRefs) {
6432
- const bytes = await fs10.readFile(attachmentRef.absolutePath);
6553
+ const bytes = await fs11.readFile(attachmentRef.absolutePath);
6433
6554
  attachments.push({
6434
6555
  relativePath: sanitizeAssetRelativePath(attachmentRef.relativeRef),
6435
6556
  mimeType: guessMimeType(attachmentRef.absolutePath),
@@ -6438,7 +6559,7 @@ async function prepareInboxHtmlInput(absolutePath, attachmentRefs) {
6438
6559
  });
6439
6560
  }
6440
6561
  const contentHash = buildCompositeHash(originalBytes, attachments);
6441
- const fallbackTitle = path11.basename(absolutePath, path11.extname(absolutePath));
6562
+ const fallbackTitle = path12.basename(absolutePath, path12.extname(absolutePath));
6442
6563
  const title = initialConversion.title || fallbackTitle;
6443
6564
  const sourceId = `${slugify(title)}-${contentHash.slice(0, 8)}`;
6444
6565
  const replacements = new Map(
@@ -6456,7 +6577,7 @@ async function prepareInboxHtmlInput(absolutePath, attachmentRefs) {
6456
6577
  sourceKind: "html",
6457
6578
  originalPath: toPosix(absolutePath),
6458
6579
  mimeType: "text/html",
6459
- storedExtension: path11.extname(absolutePath) || ".html",
6580
+ storedExtension: path12.extname(absolutePath) || ".html",
6460
6581
  payloadBytes: Buffer.from(rewrittenHtml, "utf8"),
6461
6582
  extractedText: converted.markdown,
6462
6583
  extractionArtifact,
@@ -6468,14 +6589,16 @@ async function prepareInboxHtmlInput(absolutePath, attachmentRefs) {
6468
6589
  function isSupportedInboxKind(sourceKind) {
6469
6590
  return ["markdown", "text", "html", "pdf", "docx", "image"].includes(sourceKind);
6470
6591
  }
6471
- async function ingestInput(rootDir, input, options) {
6592
+ async function ingestInputDetailed(rootDir, input, options) {
6472
6593
  const { paths } = await initWorkspace(rootDir);
6473
6594
  const normalizedOptions = normalizeIngestOptions(options);
6474
- const absoluteInput = path11.resolve(rootDir, input);
6475
- const repoRoot = isHttpUrl(input) || normalizedOptions.repoRoot ? normalizedOptions.repoRoot : await findNearestGitRoot2(absoluteInput).then((value) => value ?? path11.dirname(absoluteInput));
6595
+ const absoluteInput = path12.resolve(rootDir, input);
6596
+ const repoRoot = isHttpUrl(input) || normalizedOptions.repoRoot ? normalizedOptions.repoRoot : await findNearestGitRoot2(absoluteInput).then((value) => value ?? path12.dirname(absoluteInput));
6476
6597
  const prepared = isHttpUrl(input) ? await prepareUrlInput(rootDir, input, normalizedOptions) : await prepareFileInput(rootDir, absoluteInput, repoRoot);
6477
- const result = await persistPreparedInput(rootDir, prepared, paths);
6478
- return result.manifest;
6598
+ return await persistPreparedInput(rootDir, prepared, paths);
6599
+ }
6600
+ async function ingestInput(rootDir, input, options) {
6601
+ return (await ingestInputDetailed(rootDir, input, options)).manifest;
6479
6602
  }
6480
6603
  async function addInput(rootDir, input, options = {}) {
6481
6604
  const { paths } = await initWorkspace(rootDir);
@@ -6562,7 +6685,7 @@ async function addInput(rootDir, input, options = {}) {
6562
6685
  async function ingestDirectory(rootDir, inputDir, options) {
6563
6686
  const { paths } = await initWorkspace(rootDir);
6564
6687
  const normalizedOptions = await resolveRepoIngestOptions(rootDir, options);
6565
- const absoluteInputDir = path11.resolve(rootDir, inputDir);
6688
+ const absoluteInputDir = path12.resolve(rootDir, inputDir);
6566
6689
  const repoRoot = normalizedOptions.repoRoot ?? await findNearestGitRoot2(absoluteInputDir) ?? absoluteInputDir;
6567
6690
  if (!await fileExists(absoluteInputDir)) {
6568
6691
  throw new Error(`Directory not found: ${absoluteInputDir}`);
@@ -6572,7 +6695,7 @@ async function ingestDirectory(rootDir, inputDir, options) {
6572
6695
  const updated = [];
6573
6696
  const progress = createProgressReporter("ingest", files.length);
6574
6697
  for (const absolutePath of files) {
6575
- const relativePath = repoRelativePathFor(absolutePath, repoRoot) ?? toPosix(path11.relative(repoRoot, absolutePath));
6698
+ const relativePath = repoRelativePathFor(absolutePath, repoRoot) ?? toPosix(path12.relative(repoRoot, absolutePath));
6576
6699
  const prepared = await prepareFileInput(rootDir, absolutePath, repoRoot, sourceClassForRelativePath(relativePath, normalizedOptions));
6577
6700
  const result = await persistPreparedInput(rootDir, prepared, paths);
6578
6701
  if (result.isNew) {
@@ -6580,13 +6703,13 @@ async function ingestDirectory(rootDir, inputDir, options) {
6580
6703
  } else if (result.wasUpdated) {
6581
6704
  updated.push(result.manifest);
6582
6705
  } else {
6583
- skipped.push({ path: toPosix(path11.relative(rootDir, absolutePath)), reason: "duplicate_content" });
6706
+ skipped.push({ path: toPosix(path12.relative(rootDir, absolutePath)), reason: "duplicate_content" });
6584
6707
  }
6585
6708
  progress.tick();
6586
6709
  }
6587
6710
  progress.finish(`imported=${imported.length}, updated=${updated.length}, skipped=${skipped.length}`);
6588
- await appendLogEntry(rootDir, "ingest_directory", toPosix(path11.relative(rootDir, absoluteInputDir)) || ".", [
6589
- `repo_root=${toPosix(path11.relative(rootDir, repoRoot)) || "."}`,
6711
+ await appendLogEntry(rootDir, "ingest_directory", toPosix(path12.relative(rootDir, absoluteInputDir)) || ".", [
6712
+ `repo_root=${toPosix(path12.relative(rootDir, repoRoot)) || "."}`,
6590
6713
  `scanned=${files.length}`,
6591
6714
  `imported=${imported.length}`,
6592
6715
  `updated=${updated.length}`,
@@ -6603,7 +6726,7 @@ async function ingestDirectory(rootDir, inputDir, options) {
6603
6726
  }
6604
6727
  async function importInbox(rootDir, inputDir) {
6605
6728
  const { paths } = await initWorkspace(rootDir);
6606
- const effectiveInputDir = path11.resolve(rootDir, inputDir ?? paths.inboxDir);
6729
+ const effectiveInputDir = path12.resolve(rootDir, inputDir ?? paths.inboxDir);
6607
6730
  if (!await fileExists(effectiveInputDir)) {
6608
6731
  throw new Error(`Inbox directory not found: ${effectiveInputDir}`);
6609
6732
  }
@@ -6614,31 +6737,31 @@ async function importInbox(rootDir, inputDir) {
6614
6737
  const skipped = [];
6615
6738
  let attachmentCount = 0;
6616
6739
  for (const absolutePath of files) {
6617
- const basename = path11.basename(absolutePath);
6740
+ const basename = path12.basename(absolutePath);
6618
6741
  if (basename.startsWith(".")) {
6619
- skipped.push({ path: toPosix(path11.relative(rootDir, absolutePath)), reason: "hidden_file" });
6742
+ skipped.push({ path: toPosix(path12.relative(rootDir, absolutePath)), reason: "hidden_file" });
6620
6743
  continue;
6621
6744
  }
6622
6745
  if (claimedAttachments.has(absolutePath)) {
6623
- skipped.push({ path: toPosix(path11.relative(rootDir, absolutePath)), reason: "referenced_attachment" });
6746
+ skipped.push({ path: toPosix(path12.relative(rootDir, absolutePath)), reason: "referenced_attachment" });
6624
6747
  continue;
6625
6748
  }
6626
6749
  const mimeType = guessMimeType(absolutePath);
6627
6750
  const sourceKind = inferKind(mimeType, absolutePath);
6628
6751
  if (!isSupportedInboxKind(sourceKind)) {
6629
- skipped.push({ path: toPosix(path11.relative(rootDir, absolutePath)), reason: `unsupported_kind:${sourceKind}` });
6752
+ skipped.push({ path: toPosix(path12.relative(rootDir, absolutePath)), reason: `unsupported_kind:${sourceKind}` });
6630
6753
  continue;
6631
6754
  }
6632
6755
  const prepared = sourceKind === "markdown" && refsBySource.has(absolutePath) ? await prepareInboxMarkdownInput(absolutePath, refsBySource.get(absolutePath) ?? []) : sourceKind === "html" && refsBySource.has(absolutePath) ? await prepareInboxHtmlInput(absolutePath, refsBySource.get(absolutePath) ?? []) : await prepareFileInput(rootDir, absolutePath);
6633
6756
  const result = await persistPreparedInput(rootDir, prepared, paths);
6634
6757
  if (!result.isNew) {
6635
- skipped.push({ path: toPosix(path11.relative(rootDir, absolutePath)), reason: "duplicate_content" });
6758
+ skipped.push({ path: toPosix(path12.relative(rootDir, absolutePath)), reason: "duplicate_content" });
6636
6759
  continue;
6637
6760
  }
6638
6761
  attachmentCount += result.manifest.attachments?.length ?? 0;
6639
6762
  imported.push(result.manifest);
6640
6763
  }
6641
- await appendLogEntry(rootDir, "inbox_import", toPosix(path11.relative(rootDir, effectiveInputDir)) || ".", [
6764
+ await appendLogEntry(rootDir, "inbox_import", toPosix(path12.relative(rootDir, effectiveInputDir)) || ".", [
6642
6765
  `scanned=${files.length}`,
6643
6766
  `imported=${imported.length}`,
6644
6767
  `attachments=${attachmentCount}`,
@@ -6657,27 +6780,36 @@ async function listManifests(rootDir) {
6657
6780
  if (!await fileExists(paths.manifestsDir)) {
6658
6781
  return [];
6659
6782
  }
6660
- const entries = await fs10.readdir(paths.manifestsDir);
6783
+ const entries = await fs11.readdir(paths.manifestsDir);
6661
6784
  const manifests = await Promise.all(
6662
- entries.filter((entry) => entry.endsWith(".json")).map((entry) => readJsonFile(path11.join(paths.manifestsDir, entry)))
6785
+ entries.filter((entry) => entry.endsWith(".json")).map((entry) => readJsonFile(path12.join(paths.manifestsDir, entry)))
6663
6786
  );
6664
6787
  return manifests.filter((manifest) => Boolean(manifest));
6665
6788
  }
6789
+ async function removeManifestBySourceId(rootDir, sourceId) {
6790
+ const { paths } = await initWorkspace(rootDir);
6791
+ const manifest = await readJsonFile(path12.join(paths.manifestsDir, `${sourceId}.json`));
6792
+ if (!manifest) {
6793
+ return null;
6794
+ }
6795
+ await removeManifestArtifacts(rootDir, manifest, paths);
6796
+ return manifest;
6797
+ }
6666
6798
  async function readExtractedText(rootDir, manifest) {
6667
6799
  if (!manifest.extractedTextPath) {
6668
6800
  return void 0;
6669
6801
  }
6670
- const absolutePath = path11.resolve(rootDir, manifest.extractedTextPath);
6802
+ const absolutePath = path12.resolve(rootDir, manifest.extractedTextPath);
6671
6803
  if (!await fileExists(absolutePath)) {
6672
6804
  return void 0;
6673
6805
  }
6674
- return fs10.readFile(absolutePath, "utf8");
6806
+ return fs11.readFile(absolutePath, "utf8");
6675
6807
  }
6676
6808
  async function readExtractionArtifact(rootDir, manifest) {
6677
6809
  if (!manifest.extractedMetadataPath) {
6678
6810
  return void 0;
6679
6811
  }
6680
- const absolutePath = path11.resolve(rootDir, manifest.extractedMetadataPath);
6812
+ const absolutePath = path12.resolve(rootDir, manifest.extractedMetadataPath);
6681
6813
  if (!await fileExists(absolutePath)) {
6682
6814
  return void 0;
6683
6815
  }
@@ -6685,20 +6817,20 @@ async function readExtractionArtifact(rootDir, manifest) {
6685
6817
  }
6686
6818
 
6687
6819
  // src/mcp.ts
6688
- import fs18 from "fs/promises";
6689
- import path22 from "path";
6820
+ import fs19 from "fs/promises";
6821
+ import path23 from "path";
6690
6822
  import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
6691
6823
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
6692
6824
  import { z as z8 } from "zod";
6693
6825
 
6694
6826
  // src/schema.ts
6695
- import fs11 from "fs/promises";
6696
- import path12 from "path";
6827
+ import fs12 from "fs/promises";
6828
+ import path13 from "path";
6697
6829
  function normalizeSchemaContent(content) {
6698
6830
  return content.trim() ? content.trim() : defaultVaultSchema().trim();
6699
6831
  }
6700
6832
  async function readSchemaFile(schemaPath, fallback = defaultVaultSchema()) {
6701
- const content = await fileExists(schemaPath) ? await fs11.readFile(schemaPath, "utf8") : fallback;
6833
+ const content = await fileExists(schemaPath) ? await fs12.readFile(schemaPath, "utf8") : fallback;
6702
6834
  const normalized = normalizeSchemaContent(content);
6703
6835
  return {
6704
6836
  path: schemaPath,
@@ -6707,7 +6839,7 @@ async function readSchemaFile(schemaPath, fallback = defaultVaultSchema()) {
6707
6839
  };
6708
6840
  }
6709
6841
  function resolveProjectSchemaPath(rootDir, schemaPath) {
6710
- return path12.resolve(rootDir, schemaPath);
6842
+ return path13.resolve(rootDir, schemaPath);
6711
6843
  }
6712
6844
  function composeVaultSchema(root, projectSchemas = []) {
6713
6845
  if (!projectSchemas.length) {
@@ -6723,7 +6855,7 @@ function composeVaultSchema(root, projectSchemas = []) {
6723
6855
  (schema) => [
6724
6856
  `## Project Schema`,
6725
6857
  "",
6726
- `Path: ${toPosix(path12.relative(path12.dirname(root.path), schema.path) || schema.path)}`,
6858
+ `Path: ${toPosix(path13.relative(path13.dirname(root.path), schema.path) || schema.path)}`,
6727
6859
  "",
6728
6860
  schema.content
6729
6861
  ].join("\n")
@@ -6799,13 +6931,13 @@ function buildSchemaPrompt(schema, instruction) {
6799
6931
  }
6800
6932
 
6801
6933
  // src/vault.ts
6802
- import fs17 from "fs/promises";
6803
- import path21 from "path";
6934
+ import fs18 from "fs/promises";
6935
+ import path22 from "path";
6804
6936
  import matter9 from "gray-matter";
6805
6937
  import { z as z7 } from "zod";
6806
6938
 
6807
6939
  // src/analysis.ts
6808
- import path13 from "path";
6940
+ import path14 from "path";
6809
6941
  import { z as z2 } from "zod";
6810
6942
  var ANALYSIS_FORMAT_VERSION = 6;
6811
6943
  var sourceAnalysisSchema = z2.object({
@@ -7034,7 +7166,7 @@ function extractionWarningSummary(manifest, extraction) {
7034
7166
  return `Imported ${manifest.sourceKind} source. Text extraction is not yet available for this source.`;
7035
7167
  }
7036
7168
  async function analyzeSource(manifest, extractedText, provider, paths, schema) {
7037
- const cachePath = path13.join(paths.analysesDir, `${manifest.sourceId}.json`);
7169
+ const cachePath = path14.join(paths.analysesDir, `${manifest.sourceId}.json`);
7038
7170
  const cached = await readJsonFile(cachePath);
7039
7171
  if (cached && cached.analysisVersion === ANALYSIS_FORMAT_VERSION && cached.sourceHash === manifest.contentHash && cached.extractionHash === manifest.extractionHash && cached.schemaHash === schema.hash) {
7040
7172
  return cached;
@@ -7124,8 +7256,8 @@ function conflictConfidence(claimA, claimB) {
7124
7256
  }
7125
7257
 
7126
7258
  // src/deep-lint.ts
7127
- import fs12 from "fs/promises";
7128
- import path16 from "path";
7259
+ import fs13 from "fs/promises";
7260
+ import path17 from "path";
7129
7261
  import matter4 from "gray-matter";
7130
7262
  import { z as z5 } from "zod";
7131
7263
 
@@ -7146,7 +7278,7 @@ function normalizeFindingSeverity(value) {
7146
7278
 
7147
7279
  // src/orchestration.ts
7148
7280
  import { spawn } from "child_process";
7149
- import path14 from "path";
7281
+ import path15 from "path";
7150
7282
  import { z as z3 } from "zod";
7151
7283
  var orchestrationRoleResultSchema = z3.object({
7152
7284
  summary: z3.string().optional(),
@@ -7239,7 +7371,7 @@ async function runProviderRole(rootDir, role, roleConfig, input) {
7239
7371
  }
7240
7372
  async function runCommandRole(rootDir, role, executor, input) {
7241
7373
  const [command, ...args] = executor.command;
7242
- const cwd = executor.cwd ? path14.resolve(rootDir, executor.cwd) : rootDir;
7374
+ const cwd = executor.cwd ? path15.resolve(rootDir, executor.cwd) : rootDir;
7243
7375
  const child = spawn(command, args, {
7244
7376
  cwd,
7245
7377
  env: {
@@ -7333,7 +7465,7 @@ function summarizeRoleQuestions(results) {
7333
7465
  }
7334
7466
 
7335
7467
  // src/web-search/registry.ts
7336
- import path15 from "path";
7468
+ import path16 from "path";
7337
7469
  import { pathToFileURL as pathToFileURL2 } from "url";
7338
7470
  import { z as z4 } from "zod";
7339
7471
 
@@ -7431,7 +7563,7 @@ async function createWebSearchAdapter(id, config, rootDir) {
7431
7563
  if (!config.module) {
7432
7564
  throw new Error(`Web search provider ${id} is type "custom" but no module path was configured.`);
7433
7565
  }
7434
- const resolvedModule = path15.isAbsolute(config.module) ? config.module : path15.resolve(rootDir, config.module);
7566
+ const resolvedModule = path16.isAbsolute(config.module) ? config.module : path16.resolve(rootDir, config.module);
7435
7567
  const loaded = await import(pathToFileURL2(resolvedModule).href);
7436
7568
  const parsed = customWebSearchModuleSchema.parse(loaded);
7437
7569
  return parsed.createAdapter(id, config, rootDir);
@@ -7498,8 +7630,8 @@ async function loadContextPages(rootDir, graph) {
7498
7630
  );
7499
7631
  return Promise.all(
7500
7632
  contextPages.slice(0, 18).map(async (page) => {
7501
- const absolutePath = path16.join(paths.wikiDir, page.path);
7502
- const raw = await fs12.readFile(absolutePath, "utf8").catch(() => "");
7633
+ const absolutePath = path17.join(paths.wikiDir, page.path);
7634
+ const raw = await fs13.readFile(absolutePath, "utf8").catch(() => "");
7503
7635
  const parsed = matter4(raw);
7504
7636
  return {
7505
7637
  id: page.id,
@@ -7547,7 +7679,7 @@ function heuristicDeepFindings(contextPages, structuralFindings, graph) {
7547
7679
  code: "missing_citation",
7548
7680
  message: finding.message,
7549
7681
  pagePath: finding.pagePath,
7550
- suggestedQuery: finding.pagePath ? `Which sources support the claims in ${path16.basename(finding.pagePath, ".md")}?` : void 0
7682
+ suggestedQuery: finding.pagePath ? `Which sources support the claims in ${path17.basename(finding.pagePath, ".md")}?` : void 0
7551
7683
  });
7552
7684
  }
7553
7685
  for (const page of contextPages.filter((item) => item.kind === "source").slice(0, 3)) {
@@ -7726,8 +7858,8 @@ async function runDeepLint(rootDir, structuralFindings, options = {}) {
7726
7858
  }
7727
7859
 
7728
7860
  // src/embeddings.ts
7729
- import fs13 from "fs/promises";
7730
- import path17 from "path";
7861
+ import fs14 from "fs/promises";
7862
+ import path18 from "path";
7731
7863
  var MAX_EMBEDDING_BATCH = 32;
7732
7864
  var MAX_SIMILARITY_NODES = 240;
7733
7865
  function cosineSimilarity(left, right) {
@@ -7761,8 +7893,8 @@ async function loadPageContents(rootDir, graph) {
7761
7893
  const contents = /* @__PURE__ */ new Map();
7762
7894
  await Promise.all(
7763
7895
  graph.pages.map(async (page) => {
7764
- const absolutePath = path17.join(paths.wikiDir, page.path);
7765
- const content = await fs13.readFile(absolutePath, "utf8").catch(() => {
7896
+ const absolutePath = path18.join(paths.wikiDir, page.path);
7897
+ const content = await fs14.readFile(absolutePath, "utf8").catch(() => {
7766
7898
  process.stderr.write(`[swarmvault] Warning: could not read page ${page.path} for embedding
7767
7899
  `);
7768
7900
  return "";
@@ -9264,15 +9396,15 @@ function sourceTypeForNode(node, pagesById) {
9264
9396
  return pagesById.get(node.pageId)?.sourceType;
9265
9397
  }
9266
9398
  function supportingPathDetails(graph, edge) {
9267
- const path26 = shortestGraphPath(graph, edge.source, edge.target);
9399
+ const path28 = shortestGraphPath(graph, edge.source, edge.target);
9268
9400
  const edgesById = new Map(graph.edges.map((item) => [item.id, item]));
9269
- const pathEdges = path26.edgeIds.map((edgeId) => edgesById.get(edgeId)).filter((item) => Boolean(item));
9401
+ const pathEdges = path28.edgeIds.map((edgeId) => edgesById.get(edgeId)).filter((item) => Boolean(item));
9270
9402
  return {
9271
- pathNodeIds: path26.nodeIds,
9272
- pathEdgeIds: path26.edgeIds,
9403
+ pathNodeIds: path28.nodeIds,
9404
+ pathEdgeIds: path28.edgeIds,
9273
9405
  pathRelations: pathEdges.map((item) => item.relation),
9274
9406
  pathEvidenceClasses: pathEdges.map((item) => item.evidenceClass),
9275
- pathSummary: path26.summary
9407
+ pathSummary: path28.summary
9276
9408
  };
9277
9409
  }
9278
9410
  function surpriseScore(edge, graph, pagesById, hyperedgesByNodeId) {
@@ -9341,7 +9473,7 @@ function topSurprisingConnections(graph, pagesById) {
9341
9473
  }).map((edge) => {
9342
9474
  const source = nodesById.get(edge.source);
9343
9475
  const target = nodesById.get(edge.target);
9344
- const path26 = supportingPathDetails(graph, edge);
9476
+ const path28 = supportingPathDetails(graph, edge);
9345
9477
  const scored = surpriseScore(edge, graph, pagesById, hyperedgesByNodeId);
9346
9478
  return {
9347
9479
  id: edge.id,
@@ -9352,11 +9484,11 @@ function topSurprisingConnections(graph, pagesById) {
9352
9484
  relation: edge.relation,
9353
9485
  evidenceClass: edge.evidenceClass,
9354
9486
  confidence: edge.confidence,
9355
- pathNodeIds: path26.pathNodeIds,
9356
- pathEdgeIds: path26.pathEdgeIds,
9357
- pathRelations: path26.pathRelations,
9358
- pathEvidenceClasses: path26.pathEvidenceClasses,
9359
- pathSummary: path26.pathSummary,
9487
+ pathNodeIds: path28.pathNodeIds,
9488
+ pathEdgeIds: path28.pathEdgeIds,
9489
+ pathRelations: path28.pathRelations,
9490
+ pathEvidenceClasses: path28.pathEvidenceClasses,
9491
+ pathSummary: path28.pathSummary,
9360
9492
  why: scored.why,
9361
9493
  explanation: scored.explanation,
9362
9494
  surpriseScore: scored.score
@@ -10283,13 +10415,13 @@ function buildOutputAssetManifest(input) {
10283
10415
  }
10284
10416
 
10285
10417
  // src/outputs.ts
10286
- import fs15 from "fs/promises";
10287
- import path19 from "path";
10418
+ import fs16 from "fs/promises";
10419
+ import path20 from "path";
10288
10420
  import matter7 from "gray-matter";
10289
10421
 
10290
10422
  // src/pages.ts
10291
- import fs14 from "fs/promises";
10292
- import path18 from "path";
10423
+ import fs15 from "fs/promises";
10424
+ import path19 from "path";
10293
10425
  import matter6 from "gray-matter";
10294
10426
  function normalizeStringArray(value) {
10295
10427
  return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
@@ -10367,7 +10499,7 @@ async function loadExistingManagedPageState(absolutePath, defaults = {}) {
10367
10499
  updatedAt: updatedFallback
10368
10500
  };
10369
10501
  }
10370
- const content = await fs14.readFile(absolutePath, "utf8");
10502
+ const content = await fs15.readFile(absolutePath, "utf8");
10371
10503
  const parsed = matter6(content);
10372
10504
  return {
10373
10505
  status: normalizePageStatus(parsed.data.status, defaults.status ?? "active"),
@@ -10406,7 +10538,7 @@ function parseStoredPage(relativePath, content, defaults = {}) {
10406
10538
  const now = (/* @__PURE__ */ new Date()).toISOString();
10407
10539
  const fallbackCreatedAt = defaults.createdAt ?? now;
10408
10540
  const fallbackUpdatedAt = defaults.updatedAt ?? fallbackCreatedAt;
10409
- const title = typeof parsed.data.title === "string" ? parsed.data.title : path18.basename(relativePath, ".md");
10541
+ const title = typeof parsed.data.title === "string" ? parsed.data.title : path19.basename(relativePath, ".md");
10410
10542
  const kind = inferPageKind(relativePath, parsed.data.kind);
10411
10543
  const sourceIds = normalizeStringArray(parsed.data.source_ids);
10412
10544
  const projectIds = normalizeProjectIds(parsed.data.project_ids);
@@ -10447,18 +10579,18 @@ function parseStoredPage(relativePath, content, defaults = {}) {
10447
10579
  };
10448
10580
  }
10449
10581
  async function loadInsightPages(wikiDir) {
10450
- const insightsDir = path18.join(wikiDir, "insights");
10582
+ const insightsDir = path19.join(wikiDir, "insights");
10451
10583
  if (!await fileExists(insightsDir)) {
10452
10584
  return [];
10453
10585
  }
10454
- const files = (await listFilesRecursive(insightsDir)).filter((filePath) => filePath.endsWith(".md")).filter((filePath) => path18.basename(filePath) !== "index.md").sort((left, right) => left.localeCompare(right));
10586
+ const files = (await listFilesRecursive(insightsDir)).filter((filePath) => filePath.endsWith(".md")).filter((filePath) => path19.basename(filePath) !== "index.md").sort((left, right) => left.localeCompare(right));
10455
10587
  const insights = [];
10456
10588
  for (const absolutePath of files) {
10457
- const relativePath = toPosix(path18.relative(wikiDir, absolutePath));
10458
- const content = await fs14.readFile(absolutePath, "utf8");
10589
+ const relativePath = toPosix(path19.relative(wikiDir, absolutePath));
10590
+ const content = await fs15.readFile(absolutePath, "utf8");
10459
10591
  const parsed = matter6(content);
10460
- const stats = await fs14.stat(absolutePath);
10461
- const title = typeof parsed.data.title === "string" ? parsed.data.title : path18.basename(absolutePath, ".md");
10592
+ const stats = await fs15.stat(absolutePath);
10593
+ const title = typeof parsed.data.title === "string" ? parsed.data.title : path19.basename(absolutePath, ".md");
10462
10594
  const sourceIds = normalizeStringArray(parsed.data.source_ids);
10463
10595
  const projectIds = normalizeProjectIds(parsed.data.project_ids);
10464
10596
  const nodeIds = normalizeStringArray(parsed.data.node_ids);
@@ -10521,79 +10653,94 @@ function relatedOutputsForPage(targetPage, outputPages) {
10521
10653
  return outputPages.map((page) => ({ page, rank: relationRank(page, targetPage) })).filter((item) => item.rank > 0).sort((left, right) => right.rank - left.rank || left.page.title.localeCompare(right.page.title)).map((item) => item.page);
10522
10654
  }
10523
10655
  async function resolveUniqueOutputSlug(wikiDir, baseSlug) {
10524
- const outputsDir = path19.join(wikiDir, "outputs");
10656
+ const outputsDir = path20.join(wikiDir, "outputs");
10525
10657
  const root = baseSlug || "output";
10526
10658
  let candidate = root;
10527
10659
  let counter = 2;
10528
- while (await fileExists(path19.join(outputsDir, `${candidate}.md`))) {
10660
+ while (await fileExists(path20.join(outputsDir, `${candidate}.md`))) {
10529
10661
  candidate = `${root}-${counter}`;
10530
10662
  counter++;
10531
10663
  }
10532
10664
  return candidate;
10533
10665
  }
10534
10666
  async function loadSavedOutputPages(wikiDir) {
10535
- const outputsDir = path19.join(wikiDir, "outputs");
10536
- const entries = await fs15.readdir(outputsDir, { withFileTypes: true }).catch(() => []);
10667
+ const outputsDir = path20.join(wikiDir, "outputs");
10668
+ const entries = await fs16.readdir(outputsDir, { withFileTypes: true }).catch(() => []);
10537
10669
  const outputs = [];
10538
- for (const entry of entries) {
10539
- if (!entry.isFile() || !entry.name.endsWith(".md") || entry.name === "index.md") {
10670
+ const queue = [{ absoluteDir: outputsDir, relativeDir: "outputs" }];
10671
+ while (queue.length > 0) {
10672
+ const current = queue.shift();
10673
+ if (!current) {
10540
10674
  continue;
10541
10675
  }
10542
- const relativePath = path19.posix.join("outputs", entry.name);
10543
- const absolutePath = path19.join(outputsDir, entry.name);
10544
- const content = await fs15.readFile(absolutePath, "utf8");
10545
- const parsed = matter7(content);
10546
- const slug = entry.name.replace(/\.md$/, "");
10547
- const title = typeof parsed.data.title === "string" ? parsed.data.title : slug;
10548
- const pageId = typeof parsed.data.page_id === "string" ? parsed.data.page_id : `output:${slug}`;
10549
- const sourceIds = normalizeStringArray(parsed.data.source_ids);
10550
- const projectIds = normalizeProjectIds(parsed.data.project_ids);
10551
- const nodeIds = normalizeStringArray(parsed.data.node_ids);
10552
- const relatedPageIds = normalizeStringArray(parsed.data.related_page_ids);
10553
- const relatedNodeIds = normalizeStringArray(parsed.data.related_node_ids);
10554
- const relatedSourceIds = normalizeStringArray(parsed.data.related_source_ids);
10555
- const backlinks = normalizeStringArray(parsed.data.backlinks);
10556
- const compiledFrom = normalizeStringArray(parsed.data.compiled_from);
10557
- const stats = await fs15.stat(absolutePath);
10558
- const createdAt = typeof parsed.data.created_at === "string" ? parsed.data.created_at : stats.birthtimeMs > 0 ? stats.birthtime.toISOString() : stats.mtime.toISOString();
10559
- const updatedAt = typeof parsed.data.updated_at === "string" ? parsed.data.updated_at : stats.mtime.toISOString();
10560
- outputs.push({
10561
- page: {
10562
- id: pageId,
10563
- path: relativePath,
10564
- title,
10565
- kind: "output",
10566
- sourceIds,
10567
- projectIds,
10568
- nodeIds,
10569
- freshness: parsed.data.freshness === "stale" ? "stale" : "fresh",
10570
- status: normalizePageStatus(parsed.data.status, "active"),
10571
- confidence: typeof parsed.data.confidence === "number" ? parsed.data.confidence : 0.74,
10572
- backlinks,
10573
- schemaHash: typeof parsed.data.schema_hash === "string" ? parsed.data.schema_hash : "",
10574
- sourceHashes: normalizeSourceHashes(parsed.data.source_hashes),
10575
- relatedPageIds,
10576
- relatedNodeIds,
10577
- relatedSourceIds,
10578
- createdAt,
10579
- updatedAt,
10580
- compiledFrom: compiledFrom.length ? compiledFrom : relatedSourceIds,
10581
- managedBy: normalizePageManager(parsed.data.managed_by, "system"),
10582
- origin: typeof parsed.data.origin === "string" ? parsed.data.origin : void 0,
10583
- question: typeof parsed.data.question === "string" ? parsed.data.question : void 0,
10584
- outputFormat: parsed.data.output_format === "report" || parsed.data.output_format === "slides" || parsed.data.output_format === "chart" || parsed.data.output_format === "image" ? parsed.data.output_format : "markdown",
10585
- outputAssets: normalizeOutputAssets(parsed.data.output_assets)
10586
- },
10587
- content,
10588
- contentHash: sha256(content)
10589
- });
10676
+ const currentEntries = current.absoluteDir === outputsDir ? entries : await fs16.readdir(current.absoluteDir, { withFileTypes: true }).catch(() => []);
10677
+ for (const entry of currentEntries) {
10678
+ if (entry.isDirectory()) {
10679
+ queue.push({
10680
+ absoluteDir: path20.join(current.absoluteDir, entry.name),
10681
+ relativeDir: path20.posix.join(current.relativeDir, entry.name)
10682
+ });
10683
+ continue;
10684
+ }
10685
+ if (!entry.isFile() || !entry.name.endsWith(".md") || entry.name === "index.md") {
10686
+ continue;
10687
+ }
10688
+ const relativePath = path20.posix.join(current.relativeDir, entry.name);
10689
+ const absolutePath = path20.join(current.absoluteDir, entry.name);
10690
+ const content = await fs16.readFile(absolutePath, "utf8");
10691
+ const parsed = matter7(content);
10692
+ const slug = relativePath.replace(/^outputs\//, "").replace(/\.md$/, "");
10693
+ const title = typeof parsed.data.title === "string" ? parsed.data.title : path20.basename(slug);
10694
+ const pageId = typeof parsed.data.page_id === "string" ? parsed.data.page_id : `output:${slug}`;
10695
+ const sourceIds = normalizeStringArray(parsed.data.source_ids);
10696
+ const projectIds = normalizeProjectIds(parsed.data.project_ids);
10697
+ const nodeIds = normalizeStringArray(parsed.data.node_ids);
10698
+ const relatedPageIds = normalizeStringArray(parsed.data.related_page_ids);
10699
+ const relatedNodeIds = normalizeStringArray(parsed.data.related_node_ids);
10700
+ const relatedSourceIds = normalizeStringArray(parsed.data.related_source_ids);
10701
+ const backlinks = normalizeStringArray(parsed.data.backlinks);
10702
+ const compiledFrom = normalizeStringArray(parsed.data.compiled_from);
10703
+ const stats = await fs16.stat(absolutePath);
10704
+ const createdAt = typeof parsed.data.created_at === "string" ? parsed.data.created_at : stats.birthtimeMs > 0 ? stats.birthtime.toISOString() : stats.mtime.toISOString();
10705
+ const updatedAt = typeof parsed.data.updated_at === "string" ? parsed.data.updated_at : stats.mtime.toISOString();
10706
+ outputs.push({
10707
+ page: {
10708
+ id: pageId,
10709
+ path: relativePath,
10710
+ title,
10711
+ kind: "output",
10712
+ sourceIds,
10713
+ projectIds,
10714
+ nodeIds,
10715
+ freshness: parsed.data.freshness === "stale" ? "stale" : "fresh",
10716
+ status: normalizePageStatus(parsed.data.status, "active"),
10717
+ confidence: typeof parsed.data.confidence === "number" ? parsed.data.confidence : 0.74,
10718
+ backlinks,
10719
+ schemaHash: typeof parsed.data.schema_hash === "string" ? parsed.data.schema_hash : "",
10720
+ sourceHashes: normalizeSourceHashes(parsed.data.source_hashes),
10721
+ relatedPageIds,
10722
+ relatedNodeIds,
10723
+ relatedSourceIds,
10724
+ createdAt,
10725
+ updatedAt,
10726
+ compiledFrom: compiledFrom.length ? compiledFrom : relatedSourceIds,
10727
+ managedBy: normalizePageManager(parsed.data.managed_by, "system"),
10728
+ origin: typeof parsed.data.origin === "string" ? parsed.data.origin : void 0,
10729
+ question: typeof parsed.data.question === "string" ? parsed.data.question : void 0,
10730
+ outputFormat: parsed.data.output_format === "report" || parsed.data.output_format === "slides" || parsed.data.output_format === "chart" || parsed.data.output_format === "image" ? parsed.data.output_format : "markdown",
10731
+ outputAssets: normalizeOutputAssets(parsed.data.output_assets)
10732
+ },
10733
+ content,
10734
+ contentHash: sha256(content)
10735
+ });
10736
+ }
10590
10737
  }
10591
10738
  return outputs.sort((left, right) => left.page.title.localeCompare(right.page.title));
10592
10739
  }
10593
10740
 
10594
10741
  // src/search.ts
10595
- import fs16 from "fs/promises";
10596
- import path20 from "path";
10742
+ import fs17 from "fs/promises";
10743
+ import path21 from "path";
10597
10744
  import matter8 from "gray-matter";
10598
10745
  function getDatabaseSync() {
10599
10746
  const builtin = process.getBuiltinModule?.("node:sqlite");
@@ -10619,7 +10766,7 @@ function normalizeSourceClass2(value) {
10619
10766
  return value === "first_party" || value === "third_party" || value === "resource" || value === "generated" ? value : void 0;
10620
10767
  }
10621
10768
  async function rebuildSearchIndex(dbPath, pages, wikiDir) {
10622
- await ensureDir(path20.dirname(dbPath));
10769
+ await ensureDir(path21.dirname(dbPath));
10623
10770
  const DatabaseSync = getDatabaseSync();
10624
10771
  const db = new DatabaseSync(dbPath);
10625
10772
  db.exec("PRAGMA journal_mode = WAL;");
@@ -10651,8 +10798,8 @@ async function rebuildSearchIndex(dbPath, pages, wikiDir) {
10651
10798
  "INSERT INTO pages (id, path, title, body, kind, status, source_type, source_class, project_ids, project_key) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
10652
10799
  );
10653
10800
  for (const page of pages) {
10654
- const absolutePath = path20.join(wikiDir, page.path);
10655
- const content = await fs16.readFile(absolutePath, "utf8");
10801
+ const absolutePath = path21.join(wikiDir, page.path);
10802
+ const content = await fs17.readFile(absolutePath, "utf8");
10656
10803
  const parsed = matter8(content);
10657
10804
  insertPage.run(
10658
10805
  page.id,
@@ -10801,7 +10948,7 @@ function outputFormatInstruction(format) {
10801
10948
  }
10802
10949
  }
10803
10950
  function outputAssetPath(slug, fileName) {
10804
- return toPosix(path21.join("outputs", "assets", slug, fileName));
10951
+ return toPosix(path22.join("outputs", "assets", slug, fileName));
10805
10952
  }
10806
10953
  function outputAssetId(slug, role) {
10807
10954
  return `output:${slug}:asset:${role}`;
@@ -10941,7 +11088,7 @@ async function resolveImageGenerationProvider(rootDir) {
10941
11088
  if (!providerConfig) {
10942
11089
  throw new Error(`No provider configured with id "${preferredProviderId}" for task "imageProvider".`);
10943
11090
  }
10944
- const { createProvider: createProvider2 } = await import("./registry-G7NSRYCO.js");
11091
+ const { createProvider: createProvider2 } = await import("./registry-2REAPKPO.js");
10945
11092
  return createProvider2(preferredProviderId, providerConfig, rootDir);
10946
11093
  }
10947
11094
  async function generateOutputArtifacts(rootDir, input) {
@@ -11139,7 +11286,7 @@ async function generateOutputArtifacts(rootDir, input) {
11139
11286
  };
11140
11287
  }
11141
11288
  function normalizeProjectRoot(root) {
11142
- const normalized = toPosix(path21.posix.normalize(root.replace(/\\/g, "/"))).replace(/^\.\/+/, "").replace(/\/+$/, "");
11289
+ const normalized = toPosix(path22.posix.normalize(root.replace(/\\/g, "/"))).replace(/^\.\/+/, "").replace(/\/+$/, "");
11143
11290
  return normalized;
11144
11291
  }
11145
11292
  function projectEntries(config) {
@@ -11165,10 +11312,10 @@ function manifestPathForProject(rootDir, manifest) {
11165
11312
  if (!rawPath) {
11166
11313
  return toPosix(manifest.storedPath);
11167
11314
  }
11168
- if (!path21.isAbsolute(rawPath)) {
11315
+ if (!path22.isAbsolute(rawPath)) {
11169
11316
  return normalizeProjectRoot(rawPath);
11170
11317
  }
11171
- const relative = toPosix(path21.relative(rootDir, rawPath));
11318
+ const relative = toPosix(path22.relative(rootDir, rawPath));
11172
11319
  return relative.startsWith("..") ? toPosix(rawPath) : normalizeProjectRoot(relative);
11173
11320
  }
11174
11321
  function prefixMatches(value, prefix) {
@@ -11342,7 +11489,7 @@ function pageHashes(pages) {
11342
11489
  return Object.fromEntries(pages.map((page) => [page.page.id, page.contentHash]));
11343
11490
  }
11344
11491
  async function buildManagedGraphPage(absolutePath, defaults, build) {
11345
- const existingContent = await fileExists(absolutePath) ? await fs17.readFile(absolutePath, "utf8") : null;
11492
+ const existingContent = await fileExists(absolutePath) ? await fs18.readFile(absolutePath, "utf8") : null;
11346
11493
  let existing = await loadExistingManagedPageState(absolutePath, {
11347
11494
  status: defaults.status ?? "active",
11348
11495
  managedBy: defaults.managedBy
@@ -11380,7 +11527,7 @@ async function buildManagedGraphPage(absolutePath, defaults, build) {
11380
11527
  return built;
11381
11528
  }
11382
11529
  async function buildManagedContent(absolutePath, defaults, build) {
11383
- const existingContent = await fileExists(absolutePath) ? await fs17.readFile(absolutePath, "utf8") : null;
11530
+ const existingContent = await fileExists(absolutePath) ? await fs18.readFile(absolutePath, "utf8") : null;
11384
11531
  let existing = await loadExistingManagedPageState(absolutePath, {
11385
11532
  status: defaults.status ?? "active",
11386
11533
  managedBy: defaults.managedBy
@@ -11503,7 +11650,7 @@ function resetGraphNodeMetrics(nodes) {
11503
11650
  return nodes.map(({ communityId: _communityId, degree: _degree, bridgeScore: _bridgeScore, isGodNode: _isGodNode, ...node }) => node);
11504
11651
  }
11505
11652
  function manifestRepoPath(manifest) {
11506
- return toPosix(manifest.repoRelativePath ?? path21.basename(manifest.originalPath ?? manifest.storedPath));
11653
+ return toPosix(manifest.repoRelativePath ?? path22.basename(manifest.originalPath ?? manifest.storedPath));
11507
11654
  }
11508
11655
  function goPackageScopeKey(manifest, analysis) {
11509
11656
  if (analysis.code?.language !== "go") {
@@ -11513,7 +11660,7 @@ function goPackageScopeKey(manifest, analysis) {
11513
11660
  if (!packageName) {
11514
11661
  return null;
11515
11662
  }
11516
- return `${packageName}:${path21.posix.dirname(manifestRepoPath(manifest))}`;
11663
+ return `${packageName}:${path22.posix.dirname(manifestRepoPath(manifest))}`;
11517
11664
  }
11518
11665
  function buildGoPackageSymbolLookups(analyses, manifestsById) {
11519
11666
  const lookups = /* @__PURE__ */ new Map();
@@ -11982,7 +12129,7 @@ async function buildGraphOrientationPages(graph, paths, schemaHash, previousComp
11982
12129
  const benchmark = await readJsonFile(paths.benchmarkPath);
11983
12130
  const communityRecords = [];
11984
12131
  for (const community of graph.communities ?? []) {
11985
- const absolutePath = path21.join(paths.wikiDir, "graph", "communities", `${community.id.replace(/^community:/, "")}.md`);
12132
+ const absolutePath = path22.join(paths.wikiDir, "graph", "communities", `${community.id.replace(/^community:/, "")}.md`);
11986
12133
  communityRecords.push(
11987
12134
  await buildManagedGraphPage(
11988
12135
  absolutePath,
@@ -12011,7 +12158,7 @@ async function buildGraphOrientationPages(graph, paths, schemaHash, previousComp
12011
12158
  graphHash: graphHash(graph),
12012
12159
  contradictions
12013
12160
  });
12014
- const reportAbsolutePath = path21.join(paths.wikiDir, "graph", "report.md");
12161
+ const reportAbsolutePath = path22.join(paths.wikiDir, "graph", "report.md");
12015
12162
  const reportRecord = await buildManagedGraphPage(
12016
12163
  reportAbsolutePath,
12017
12164
  {
@@ -12032,7 +12179,7 @@ async function buildGraphOrientationPages(graph, paths, schemaHash, previousComp
12032
12179
  };
12033
12180
  }
12034
12181
  async function writePage(wikiDir, relativePath, content, changedPages) {
12035
- const absolutePath = path21.resolve(wikiDir, relativePath);
12182
+ const absolutePath = path22.resolve(wikiDir, relativePath);
12036
12183
  const changed = await writeFileIfChanged(absolutePath, content);
12037
12184
  if (changed) {
12038
12185
  changedPages.push(relativePath);
@@ -12095,29 +12242,29 @@ async function requiredCompileArtifactsExist(paths) {
12095
12242
  paths.graphPath,
12096
12243
  paths.codeIndexPath,
12097
12244
  paths.searchDbPath,
12098
- path21.join(paths.wikiDir, "index.md"),
12099
- path21.join(paths.wikiDir, "sources", "index.md"),
12100
- path21.join(paths.wikiDir, "code", "index.md"),
12101
- path21.join(paths.wikiDir, "concepts", "index.md"),
12102
- path21.join(paths.wikiDir, "entities", "index.md"),
12103
- path21.join(paths.wikiDir, "outputs", "index.md"),
12104
- path21.join(paths.wikiDir, "projects", "index.md"),
12105
- path21.join(paths.wikiDir, "candidates", "index.md")
12245
+ path22.join(paths.wikiDir, "index.md"),
12246
+ path22.join(paths.wikiDir, "sources", "index.md"),
12247
+ path22.join(paths.wikiDir, "code", "index.md"),
12248
+ path22.join(paths.wikiDir, "concepts", "index.md"),
12249
+ path22.join(paths.wikiDir, "entities", "index.md"),
12250
+ path22.join(paths.wikiDir, "outputs", "index.md"),
12251
+ path22.join(paths.wikiDir, "projects", "index.md"),
12252
+ path22.join(paths.wikiDir, "candidates", "index.md")
12106
12253
  ];
12107
12254
  const checks = await Promise.all(requiredPaths.map((filePath) => fileExists(filePath)));
12108
12255
  return checks.every(Boolean);
12109
12256
  }
12110
12257
  async function loadAvailableCachedAnalyses(paths, manifests) {
12111
12258
  const analyses = await Promise.all(
12112
- manifests.map(async (manifest) => readJsonFile(path21.join(paths.analysesDir, `${manifest.sourceId}.json`)))
12259
+ manifests.map(async (manifest) => readJsonFile(path22.join(paths.analysesDir, `${manifest.sourceId}.json`)))
12113
12260
  );
12114
12261
  return analyses.filter((analysis) => Boolean(analysis));
12115
12262
  }
12116
12263
  function approvalManifestPath(paths, approvalId) {
12117
- return path21.join(paths.approvalsDir, approvalId, "manifest.json");
12264
+ return path22.join(paths.approvalsDir, approvalId, "manifest.json");
12118
12265
  }
12119
12266
  function approvalGraphPath(paths, approvalId) {
12120
- return path21.join(paths.approvalsDir, approvalId, "state", "graph.json");
12267
+ return path22.join(paths.approvalsDir, approvalId, "state", "graph.json");
12121
12268
  }
12122
12269
  async function readApprovalManifest(paths, approvalId) {
12123
12270
  const manifest = await readJsonFile(approvalManifestPath(paths, approvalId));
@@ -12127,7 +12274,7 @@ async function readApprovalManifest(paths, approvalId) {
12127
12274
  return manifest;
12128
12275
  }
12129
12276
  async function writeApprovalManifest(paths, manifest) {
12130
- await fs17.writeFile(approvalManifestPath(paths, manifest.approvalId), `${JSON.stringify(manifest, null, 2)}
12277
+ await fs18.writeFile(approvalManifestPath(paths, manifest.approvalId), `${JSON.stringify(manifest, null, 2)}
12131
12278
  `, "utf8");
12132
12279
  }
12133
12280
  async function buildApprovalEntries(paths, changedFiles, deletedPaths, previousGraph, graph) {
@@ -12142,7 +12289,7 @@ async function buildApprovalEntries(paths, changedFiles, deletedPaths, previousG
12142
12289
  continue;
12143
12290
  }
12144
12291
  const previousPage = previousPagesById.get(nextPage.id);
12145
- const currentExists = await fileExists(path21.join(paths.wikiDir, file.relativePath));
12292
+ const currentExists = await fileExists(path22.join(paths.wikiDir, file.relativePath));
12146
12293
  if (previousPage && previousPage.path !== nextPage.path) {
12147
12294
  entries.push({
12148
12295
  pageId: nextPage.id,
@@ -12175,7 +12322,7 @@ async function buildApprovalEntries(paths, changedFiles, deletedPaths, previousG
12175
12322
  const previousPage = previousPagesByPath.get(deletedPath);
12176
12323
  entries.push({
12177
12324
  pageId: previousPage?.id ?? `page:${slugify(deletedPath)}`,
12178
- title: previousPage?.title ?? path21.basename(deletedPath, ".md"),
12325
+ title: previousPage?.title ?? path22.basename(deletedPath, ".md"),
12179
12326
  kind: previousPage?.kind ?? "index",
12180
12327
  changeType: "delete",
12181
12328
  status: "pending",
@@ -12187,16 +12334,16 @@ async function buildApprovalEntries(paths, changedFiles, deletedPaths, previousG
12187
12334
  }
12188
12335
  async function stageApprovalBundle(paths, changedFiles, deletedPaths, previousGraph, graph) {
12189
12336
  const approvalId = `compile-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}`;
12190
- const approvalDir = path21.join(paths.approvalsDir, approvalId);
12337
+ const approvalDir = path22.join(paths.approvalsDir, approvalId);
12191
12338
  await ensureDir(approvalDir);
12192
- await ensureDir(path21.join(approvalDir, "wiki"));
12193
- await ensureDir(path21.join(approvalDir, "state"));
12339
+ await ensureDir(path22.join(approvalDir, "wiki"));
12340
+ await ensureDir(path22.join(approvalDir, "state"));
12194
12341
  for (const file of changedFiles) {
12195
- const targetPath = path21.join(approvalDir, "wiki", file.relativePath);
12196
- await ensureDir(path21.dirname(targetPath));
12197
- await fs17.writeFile(targetPath, file.content, "utf8");
12342
+ const targetPath = path22.join(approvalDir, "wiki", file.relativePath);
12343
+ await ensureDir(path22.dirname(targetPath));
12344
+ await fs18.writeFile(targetPath, file.content, "utf8");
12198
12345
  }
12199
- await fs17.writeFile(path21.join(approvalDir, "state", "graph.json"), JSON.stringify(graph, null, 2), "utf8");
12346
+ await fs18.writeFile(path22.join(approvalDir, "state", "graph.json"), JSON.stringify(graph, null, 2), "utf8");
12200
12347
  await writeApprovalManifest(paths, {
12201
12348
  approvalId,
12202
12349
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -12258,7 +12405,7 @@ async function syncVaultArtifacts(rootDir, input) {
12258
12405
  confidence: 1
12259
12406
  });
12260
12407
  const sourceRecord = await buildManagedGraphPage(
12261
- path21.join(paths.wikiDir, preview.path),
12408
+ path22.join(paths.wikiDir, preview.path),
12262
12409
  {
12263
12410
  managedBy: "system",
12264
12411
  confidence: 1,
@@ -12304,7 +12451,7 @@ async function syncVaultArtifacts(rootDir, input) {
12304
12451
  );
12305
12452
  records.push(
12306
12453
  await buildManagedGraphPage(
12307
- path21.join(paths.wikiDir, modulePreview.path),
12454
+ path22.join(paths.wikiDir, modulePreview.path),
12308
12455
  {
12309
12456
  managedBy: "system",
12310
12457
  confidence: 1,
@@ -12338,8 +12485,8 @@ async function syncVaultArtifacts(rootDir, input) {
12338
12485
  const relativePath = promoted ? activeAggregatePath(itemKind, slug) : candidatePagePathFor(itemKind, slug);
12339
12486
  const aggregateSourceClass2 = aggregateManifestSourceClass(input.manifests, sourceIds);
12340
12487
  const fallbackPaths = [
12341
- path21.join(paths.wikiDir, activeAggregatePath(itemKind, slug)),
12342
- path21.join(paths.wikiDir, candidatePagePathFor(itemKind, slug))
12488
+ path22.join(paths.wikiDir, activeAggregatePath(itemKind, slug)),
12489
+ path22.join(paths.wikiDir, candidatePagePathFor(itemKind, slug))
12343
12490
  ];
12344
12491
  const confidence = nodeConfidence(aggregate.sourceAnalyses.length);
12345
12492
  const preview = emptyGraphPage({
@@ -12357,7 +12504,7 @@ async function syncVaultArtifacts(rootDir, input) {
12357
12504
  status: promoted ? "active" : "candidate"
12358
12505
  });
12359
12506
  const pageRecord = await buildManagedGraphPage(
12360
- path21.join(paths.wikiDir, relativePath),
12507
+ path22.join(paths.wikiDir, relativePath),
12361
12508
  {
12362
12509
  status: promoted ? "active" : "candidate",
12363
12510
  managedBy: "system",
@@ -12474,7 +12621,7 @@ async function syncVaultArtifacts(rootDir, input) {
12474
12621
  confidence: 1
12475
12622
  }),
12476
12623
  content: await buildManagedContent(
12477
- path21.join(paths.wikiDir, "projects", "index.md"),
12624
+ path22.join(paths.wikiDir, "projects", "index.md"),
12478
12625
  {
12479
12626
  managedBy: "system",
12480
12627
  compiledFrom: indexCompiledFrom(projectIndexRefs)
@@ -12498,7 +12645,7 @@ async function syncVaultArtifacts(rootDir, input) {
12498
12645
  records.push({
12499
12646
  page: projectIndexRef,
12500
12647
  content: await buildManagedContent(
12501
- path21.join(paths.wikiDir, projectIndexRef.path),
12648
+ path22.join(paths.wikiDir, projectIndexRef.path),
12502
12649
  {
12503
12650
  managedBy: "system",
12504
12651
  compiledFrom: indexCompiledFrom(Object.values(sections).flat())
@@ -12526,7 +12673,7 @@ async function syncVaultArtifacts(rootDir, input) {
12526
12673
  confidence: 1
12527
12674
  }),
12528
12675
  content: await buildManagedContent(
12529
- path21.join(paths.wikiDir, "index.md"),
12676
+ path22.join(paths.wikiDir, "index.md"),
12530
12677
  {
12531
12678
  managedBy: "system",
12532
12679
  compiledFrom: indexCompiledFrom(allPages)
@@ -12557,7 +12704,7 @@ async function syncVaultArtifacts(rootDir, input) {
12557
12704
  confidence: 1
12558
12705
  }),
12559
12706
  content: await buildManagedContent(
12560
- path21.join(paths.wikiDir, relativePath),
12707
+ path22.join(paths.wikiDir, relativePath),
12561
12708
  {
12562
12709
  managedBy: "system",
12563
12710
  compiledFrom: indexCompiledFrom(pages)
@@ -12568,12 +12715,12 @@ async function syncVaultArtifacts(rootDir, input) {
12568
12715
  }
12569
12716
  const nextPagePaths = new Set(records.map((record) => record.page.path));
12570
12717
  const obsoleteGraphPaths = (previousGraph?.pages ?? []).filter((page) => page.kind !== "output" && page.kind !== "insight").map((page) => page.path).filter((relativePath) => !nextPagePaths.has(relativePath));
12571
- const existingProjectIndexPaths = (await listFilesRecursive(paths.projectsDir)).filter((absolutePath) => absolutePath.endsWith(".md")).map((absolutePath) => toPosix(path21.relative(paths.wikiDir, absolutePath))).filter((relativePath) => !nextPagePaths.has(relativePath));
12718
+ const existingProjectIndexPaths = (await listFilesRecursive(paths.projectsDir)).filter((absolutePath) => absolutePath.endsWith(".md")).map((absolutePath) => toPosix(path22.relative(paths.wikiDir, absolutePath))).filter((relativePath) => !nextPagePaths.has(relativePath));
12572
12719
  const obsoletePaths = uniqueStrings3([...obsoleteGraphPaths, ...existingProjectIndexPaths]);
12573
12720
  const changedFiles = [];
12574
12721
  for (const record of records) {
12575
- const absolutePath = path21.join(paths.wikiDir, record.page.path);
12576
- const current = await fileExists(absolutePath) ? await fs17.readFile(absolutePath, "utf8") : null;
12722
+ const absolutePath = path22.join(paths.wikiDir, record.page.path);
12723
+ const current = await fileExists(absolutePath) ? await fs18.readFile(absolutePath, "utf8") : null;
12577
12724
  if (current !== record.content) {
12578
12725
  changedPages.push(record.page.path);
12579
12726
  changedFiles.push({ relativePath: record.page.path, content: record.content });
@@ -12598,10 +12745,10 @@ async function syncVaultArtifacts(rootDir, input) {
12598
12745
  await writePage(paths.wikiDir, record.page.path, record.content, writeChanges);
12599
12746
  }
12600
12747
  for (const relativePath of obsoletePaths) {
12601
- await fs17.rm(path21.join(paths.wikiDir, relativePath), { force: true });
12748
+ await fs18.rm(path22.join(paths.wikiDir, relativePath), { force: true });
12602
12749
  }
12603
12750
  await writeJsonFile(paths.graphPath, graph);
12604
- await writeJsonFile(path21.join(paths.wikiDir, "graph", "report.json"), graphOrientation.report);
12751
+ await writeJsonFile(path22.join(paths.wikiDir, "graph", "report.json"), graphOrientation.report);
12605
12752
  await writeJsonFile(paths.codeIndexPath, input.codeIndex);
12606
12753
  await writeJsonFile(paths.compileStatePath, {
12607
12754
  generatedAt: graph.generatedAt,
@@ -12672,17 +12819,17 @@ async function refreshIndexesAndSearch(rootDir, pages) {
12672
12819
  })
12673
12820
  );
12674
12821
  await Promise.all([
12675
- ensureDir(path21.join(paths.wikiDir, "sources")),
12676
- ensureDir(path21.join(paths.wikiDir, "code")),
12677
- ensureDir(path21.join(paths.wikiDir, "concepts")),
12678
- ensureDir(path21.join(paths.wikiDir, "entities")),
12679
- ensureDir(path21.join(paths.wikiDir, "outputs")),
12680
- ensureDir(path21.join(paths.wikiDir, "graph")),
12681
- ensureDir(path21.join(paths.wikiDir, "graph", "communities")),
12682
- ensureDir(path21.join(paths.wikiDir, "projects")),
12683
- ensureDir(path21.join(paths.wikiDir, "candidates"))
12822
+ ensureDir(path22.join(paths.wikiDir, "sources")),
12823
+ ensureDir(path22.join(paths.wikiDir, "code")),
12824
+ ensureDir(path22.join(paths.wikiDir, "concepts")),
12825
+ ensureDir(path22.join(paths.wikiDir, "entities")),
12826
+ ensureDir(path22.join(paths.wikiDir, "outputs")),
12827
+ ensureDir(path22.join(paths.wikiDir, "graph")),
12828
+ ensureDir(path22.join(paths.wikiDir, "graph", "communities")),
12829
+ ensureDir(path22.join(paths.wikiDir, "projects")),
12830
+ ensureDir(path22.join(paths.wikiDir, "candidates"))
12684
12831
  ]);
12685
- const projectsIndexPath = path21.join(paths.wikiDir, "projects", "index.md");
12832
+ const projectsIndexPath = path22.join(paths.wikiDir, "projects", "index.md");
12686
12833
  await writeFileIfChanged(
12687
12834
  projectsIndexPath,
12688
12835
  await buildManagedContent(
@@ -12703,7 +12850,7 @@ async function refreshIndexesAndSearch(rootDir, pages) {
12703
12850
  outputs: pages.filter((page) => page.kind === "output" && page.projectIds.includes(project.id)),
12704
12851
  candidates: pages.filter((page) => page.status === "candidate" && page.projectIds.includes(project.id))
12705
12852
  };
12706
- const absolutePath = path21.join(paths.wikiDir, "projects", project.id, "index.md");
12853
+ const absolutePath = path22.join(paths.wikiDir, "projects", project.id, "index.md");
12707
12854
  await writeFileIfChanged(
12708
12855
  absolutePath,
12709
12856
  await buildManagedContent(
@@ -12721,7 +12868,7 @@ async function refreshIndexesAndSearch(rootDir, pages) {
12721
12868
  )
12722
12869
  );
12723
12870
  }
12724
- const rootIndexPath = path21.join(paths.wikiDir, "index.md");
12871
+ const rootIndexPath = path22.join(paths.wikiDir, "index.md");
12725
12872
  await writeFileIfChanged(
12726
12873
  rootIndexPath,
12727
12874
  await buildManagedContent(
@@ -12742,7 +12889,7 @@ async function refreshIndexesAndSearch(rootDir, pages) {
12742
12889
  ["candidates/index.md", "candidates", pagesWithGraph.filter((page) => page.status === "candidate")],
12743
12890
  ["graph/index.md", "graph", pagesWithGraph.filter((page) => page.kind === "graph_report" || page.kind === "community_summary")]
12744
12891
  ]) {
12745
- const absolutePath = path21.join(paths.wikiDir, relativePath);
12892
+ const absolutePath = path22.join(paths.wikiDir, relativePath);
12746
12893
  await writeFileIfChanged(
12747
12894
  absolutePath,
12748
12895
  await buildManagedContent(
@@ -12756,23 +12903,23 @@ async function refreshIndexesAndSearch(rootDir, pages) {
12756
12903
  );
12757
12904
  }
12758
12905
  for (const record of graphOrientation.records) {
12759
- await writeFileIfChanged(path21.join(paths.wikiDir, record.page.path), record.content);
12906
+ await writeFileIfChanged(path22.join(paths.wikiDir, record.page.path), record.content);
12760
12907
  }
12761
12908
  if (graphOrientation.report) {
12762
- await writeJsonFile(path21.join(paths.wikiDir, "graph", "report.json"), graphOrientation.report);
12909
+ await writeJsonFile(path22.join(paths.wikiDir, "graph", "report.json"), graphOrientation.report);
12763
12910
  }
12764
- const existingProjectIndexPaths = (await listFilesRecursive(paths.projectsDir)).filter((absolutePath) => absolutePath.endsWith(".md")).map((absolutePath) => toPosix(path21.relative(paths.wikiDir, absolutePath)));
12911
+ const existingProjectIndexPaths = (await listFilesRecursive(paths.projectsDir)).filter((absolutePath) => absolutePath.endsWith(".md")).map((absolutePath) => toPosix(path22.relative(paths.wikiDir, absolutePath)));
12765
12912
  const allowedProjectIndexPaths = /* @__PURE__ */ new Set([
12766
12913
  "projects/index.md",
12767
12914
  ...configuredProjects.map((project) => `projects/${project.id}/index.md`)
12768
12915
  ]);
12769
12916
  await Promise.all(
12770
- existingProjectIndexPaths.filter((relativePath) => !allowedProjectIndexPaths.has(relativePath)).map((relativePath) => fs17.rm(path21.join(paths.wikiDir, relativePath), { force: true }))
12917
+ existingProjectIndexPaths.filter((relativePath) => !allowedProjectIndexPaths.has(relativePath)).map((relativePath) => fs18.rm(path22.join(paths.wikiDir, relativePath), { force: true }))
12771
12918
  );
12772
- const existingGraphPages = (await listFilesRecursive(path21.join(paths.wikiDir, "graph").replace(/\/$/, "")).catch(() => [])).filter((absolutePath) => absolutePath.endsWith(".md")).map((absolutePath) => toPosix(path21.relative(paths.wikiDir, absolutePath)));
12919
+ const existingGraphPages = (await listFilesRecursive(path22.join(paths.wikiDir, "graph").replace(/\/$/, "")).catch(() => [])).filter((absolutePath) => absolutePath.endsWith(".md")).map((absolutePath) => toPosix(path22.relative(paths.wikiDir, absolutePath)));
12773
12920
  const allowedGraphPages = /* @__PURE__ */ new Set(["graph/index.md", ...graphOrientation.records.map((record) => record.page.path)]);
12774
12921
  await Promise.all(
12775
- existingGraphPages.filter((relativePath) => !allowedGraphPages.has(relativePath)).map((relativePath) => fs17.rm(path21.join(paths.wikiDir, relativePath), { force: true }))
12922
+ existingGraphPages.filter((relativePath) => !allowedGraphPages.has(relativePath)).map((relativePath) => fs18.rm(path22.join(paths.wikiDir, relativePath), { force: true }))
12776
12923
  );
12777
12924
  await rebuildSearchIndex(paths.searchDbPath, pagesWithGraph, paths.wikiDir);
12778
12925
  }
@@ -12792,7 +12939,7 @@ async function prepareOutputPageSave(rootDir, input) {
12792
12939
  confidence: 0.74
12793
12940
  }
12794
12941
  });
12795
- const absolutePath = path21.join(paths.wikiDir, output.page.path);
12942
+ const absolutePath = path22.join(paths.wikiDir, output.page.path);
12796
12943
  return {
12797
12944
  page: output.page,
12798
12945
  savedPath: absolutePath,
@@ -12804,15 +12951,15 @@ async function prepareOutputPageSave(rootDir, input) {
12804
12951
  async function persistOutputPage(rootDir, input) {
12805
12952
  const { paths } = await loadVaultConfig(rootDir);
12806
12953
  const prepared = await prepareOutputPageSave(rootDir, input);
12807
- await ensureDir(path21.dirname(prepared.savedPath));
12808
- await fs17.writeFile(prepared.savedPath, prepared.content, "utf8");
12954
+ await ensureDir(path22.dirname(prepared.savedPath));
12955
+ await fs18.writeFile(prepared.savedPath, prepared.content, "utf8");
12809
12956
  for (const assetFile of prepared.assetFiles) {
12810
- const assetPath = path21.join(paths.wikiDir, assetFile.relativePath);
12811
- await ensureDir(path21.dirname(assetPath));
12957
+ const assetPath = path22.join(paths.wikiDir, assetFile.relativePath);
12958
+ await ensureDir(path22.dirname(assetPath));
12812
12959
  if (typeof assetFile.content === "string") {
12813
- await fs17.writeFile(assetPath, assetFile.content, assetFile.encoding ?? "utf8");
12960
+ await fs18.writeFile(assetPath, assetFile.content, assetFile.encoding ?? "utf8");
12814
12961
  } else {
12815
- await fs17.writeFile(assetPath, assetFile.content);
12962
+ await fs18.writeFile(assetPath, assetFile.content);
12816
12963
  }
12817
12964
  }
12818
12965
  return { page: prepared.page, savedPath: prepared.savedPath, outputAssets: prepared.outputAssets };
@@ -12833,7 +12980,7 @@ async function prepareExploreHubSave(rootDir, input) {
12833
12980
  confidence: 0.76
12834
12981
  }
12835
12982
  });
12836
- const absolutePath = path21.join(paths.wikiDir, hub.page.path);
12983
+ const absolutePath = path22.join(paths.wikiDir, hub.page.path);
12837
12984
  return {
12838
12985
  page: hub.page,
12839
12986
  savedPath: absolutePath,
@@ -12845,15 +12992,15 @@ async function prepareExploreHubSave(rootDir, input) {
12845
12992
  async function persistExploreHub(rootDir, input) {
12846
12993
  const { paths } = await loadVaultConfig(rootDir);
12847
12994
  const prepared = await prepareExploreHubSave(rootDir, input);
12848
- await ensureDir(path21.dirname(prepared.savedPath));
12849
- await fs17.writeFile(prepared.savedPath, prepared.content, "utf8");
12995
+ await ensureDir(path22.dirname(prepared.savedPath));
12996
+ await fs18.writeFile(prepared.savedPath, prepared.content, "utf8");
12850
12997
  for (const assetFile of prepared.assetFiles) {
12851
- const assetPath = path21.join(paths.wikiDir, assetFile.relativePath);
12852
- await ensureDir(path21.dirname(assetPath));
12998
+ const assetPath = path22.join(paths.wikiDir, assetFile.relativePath);
12999
+ await ensureDir(path22.dirname(assetPath));
12853
13000
  if (typeof assetFile.content === "string") {
12854
- await fs17.writeFile(assetPath, assetFile.content, assetFile.encoding ?? "utf8");
13001
+ await fs18.writeFile(assetPath, assetFile.content, assetFile.encoding ?? "utf8");
12855
13002
  } else {
12856
- await fs17.writeFile(assetPath, assetFile.content);
13003
+ await fs18.writeFile(assetPath, assetFile.content);
12857
13004
  }
12858
13005
  }
12859
13006
  return { page: prepared.page, savedPath: prepared.savedPath, outputAssets: prepared.outputAssets };
@@ -12870,17 +13017,17 @@ async function stageOutputApprovalBundle(rootDir, stagedPages) {
12870
13017
  }))
12871
13018
  ]);
12872
13019
  const approvalId = `schedule-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}`;
12873
- const approvalDir = path21.join(paths.approvalsDir, approvalId);
13020
+ const approvalDir = path22.join(paths.approvalsDir, approvalId);
12874
13021
  await ensureDir(approvalDir);
12875
- await ensureDir(path21.join(approvalDir, "wiki"));
12876
- await ensureDir(path21.join(approvalDir, "state"));
13022
+ await ensureDir(path22.join(approvalDir, "wiki"));
13023
+ await ensureDir(path22.join(approvalDir, "state"));
12877
13024
  for (const file of changedFiles) {
12878
- const targetPath = path21.join(approvalDir, "wiki", file.relativePath);
12879
- await ensureDir(path21.dirname(targetPath));
13025
+ const targetPath = path22.join(approvalDir, "wiki", file.relativePath);
13026
+ await ensureDir(path22.dirname(targetPath));
12880
13027
  if ("binary" in file && file.binary) {
12881
- await fs17.writeFile(targetPath, Buffer.from(file.content, "base64"));
13028
+ await fs18.writeFile(targetPath, Buffer.from(file.content, "base64"));
12882
13029
  } else {
12883
- await fs17.writeFile(targetPath, file.content, "utf8");
13030
+ await fs18.writeFile(targetPath, file.content, "utf8");
12884
13031
  }
12885
13032
  }
12886
13033
  const nextPages = sortGraphPages([
@@ -12895,7 +13042,7 @@ async function stageOutputApprovalBundle(rootDir, stagedPages) {
12895
13042
  sources: previousGraph?.sources ?? [],
12896
13043
  pages: nextPages
12897
13044
  };
12898
- await fs17.writeFile(path21.join(approvalDir, "state", "graph.json"), JSON.stringify(graph, null, 2), "utf8");
13045
+ await fs18.writeFile(path22.join(approvalDir, "state", "graph.json"), JSON.stringify(graph, null, 2), "utf8");
12899
13046
  await writeApprovalManifest(paths, {
12900
13047
  approvalId,
12901
13048
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -12924,9 +13071,9 @@ async function executeQuery(rootDir, question, format) {
12924
13071
  const searchResults = searchPages(paths.searchDbPath, question, 5);
12925
13072
  const excerpts = await Promise.all(
12926
13073
  searchResults.map(async (result) => {
12927
- const absolutePath = path21.join(paths.wikiDir, result.path);
13074
+ const absolutePath = path22.join(paths.wikiDir, result.path);
12928
13075
  try {
12929
- const content = await fs17.readFile(absolutePath, "utf8");
13076
+ const content = await fs18.readFile(absolutePath, "utf8");
12930
13077
  const parsed = matter9(content);
12931
13078
  return `# ${result.title}
12932
13079
  ${truncate(normalizeWhitespace(parsed.content), 1200)}`;
@@ -13160,7 +13307,7 @@ function computeChangeSummary(current, staged, changeType) {
13160
13307
  async function listApprovals(rootDir) {
13161
13308
  const { paths } = await loadVaultConfig(rootDir);
13162
13309
  const manifests = await Promise.all(
13163
- (await fs17.readdir(paths.approvalsDir, { withFileTypes: true }).catch(() => [])).filter((entry) => entry.isDirectory()).map(async (entry) => {
13310
+ (await fs18.readdir(paths.approvalsDir, { withFileTypes: true }).catch(() => [])).filter((entry) => entry.isDirectory()).map(async (entry) => {
13164
13311
  try {
13165
13312
  return await readApprovalManifest(paths, entry.name);
13166
13313
  } catch {
@@ -13176,8 +13323,8 @@ async function readApproval(rootDir, approvalId, options) {
13176
13323
  const details = await Promise.all(
13177
13324
  manifest.entries.map(async (entry) => {
13178
13325
  const currentPath = entry.previousPath ?? entry.nextPath;
13179
- const currentContent = currentPath ? await fs17.readFile(path21.join(paths.wikiDir, currentPath), "utf8").catch(() => void 0) : void 0;
13180
- const stagedContent = entry.nextPath ? await fs17.readFile(path21.join(paths.approvalsDir, approvalId, "wiki", entry.nextPath), "utf8").catch(() => void 0) : void 0;
13326
+ const currentContent = currentPath ? await fs18.readFile(path22.join(paths.wikiDir, currentPath), "utf8").catch(() => void 0) : void 0;
13327
+ const stagedContent = entry.nextPath ? await fs18.readFile(path22.join(paths.approvalsDir, approvalId, "wiki", entry.nextPath), "utf8").catch(() => void 0) : void 0;
13181
13328
  const detail = {
13182
13329
  ...entry,
13183
13330
  currentContent,
@@ -13210,26 +13357,26 @@ async function acceptApproval(rootDir, approvalId, targets = []) {
13210
13357
  if (!entry.nextPath) {
13211
13358
  throw new Error(`Approval entry ${entry.pageId} is missing a staged path.`);
13212
13359
  }
13213
- const stagedAbsolutePath = path21.join(paths.approvalsDir, approvalId, "wiki", entry.nextPath);
13214
- const stagedContent = await fs17.readFile(stagedAbsolutePath, "utf8");
13215
- const targetAbsolutePath = path21.join(paths.wikiDir, entry.nextPath);
13216
- await ensureDir(path21.dirname(targetAbsolutePath));
13217
- await fs17.writeFile(targetAbsolutePath, stagedContent, "utf8");
13360
+ const stagedAbsolutePath = path22.join(paths.approvalsDir, approvalId, "wiki", entry.nextPath);
13361
+ const stagedContent = await fs18.readFile(stagedAbsolutePath, "utf8");
13362
+ const targetAbsolutePath = path22.join(paths.wikiDir, entry.nextPath);
13363
+ await ensureDir(path22.dirname(targetAbsolutePath));
13364
+ await fs18.writeFile(targetAbsolutePath, stagedContent, "utf8");
13218
13365
  if (entry.changeType === "promote" && entry.previousPath) {
13219
- await fs17.rm(path21.join(paths.wikiDir, entry.previousPath), { force: true });
13366
+ await fs18.rm(path22.join(paths.wikiDir, entry.previousPath), { force: true });
13220
13367
  }
13221
13368
  const nextPage = bundleGraph?.pages.find((page) => page.id === entry.pageId && page.path === entry.nextPath) ?? parseStoredPage(entry.nextPath, stagedContent);
13222
13369
  if (nextPage.kind === "output" && nextPage.outputAssets?.length) {
13223
- const outputAssetDir = path21.join(paths.wikiDir, "outputs", "assets", path21.basename(nextPage.path, ".md"));
13224
- await fs17.rm(outputAssetDir, { recursive: true, force: true });
13370
+ const outputAssetDir = path22.join(paths.wikiDir, "outputs", "assets", path22.basename(nextPage.path, ".md"));
13371
+ await fs18.rm(outputAssetDir, { recursive: true, force: true });
13225
13372
  for (const asset of nextPage.outputAssets) {
13226
- const stagedAssetPath = path21.join(paths.approvalsDir, approvalId, "wiki", asset.path);
13373
+ const stagedAssetPath = path22.join(paths.approvalsDir, approvalId, "wiki", asset.path);
13227
13374
  if (!await fileExists(stagedAssetPath)) {
13228
13375
  continue;
13229
13376
  }
13230
- const targetAssetPath = path21.join(paths.wikiDir, asset.path);
13231
- await ensureDir(path21.dirname(targetAssetPath));
13232
- await fs17.copyFile(stagedAssetPath, targetAssetPath);
13377
+ const targetAssetPath = path22.join(paths.wikiDir, asset.path);
13378
+ await ensureDir(path22.dirname(targetAssetPath));
13379
+ await fs18.copyFile(stagedAssetPath, targetAssetPath);
13233
13380
  }
13234
13381
  }
13235
13382
  nextPages = nextPages.filter(
@@ -13240,10 +13387,10 @@ async function acceptApproval(rootDir, approvalId, targets = []) {
13240
13387
  } else {
13241
13388
  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;
13242
13389
  if (entry.previousPath) {
13243
- await fs17.rm(path21.join(paths.wikiDir, entry.previousPath), { force: true });
13390
+ await fs18.rm(path22.join(paths.wikiDir, entry.previousPath), { force: true });
13244
13391
  }
13245
13392
  if (deletedPage?.kind === "output") {
13246
- await fs17.rm(path21.join(paths.wikiDir, "outputs", "assets", path21.basename(deletedPage.path, ".md")), {
13393
+ await fs18.rm(path22.join(paths.wikiDir, "outputs", "assets", path22.basename(deletedPage.path, ".md")), {
13247
13394
  recursive: true,
13248
13395
  force: true
13249
13396
  });
@@ -13334,7 +13481,7 @@ async function promoteCandidate(rootDir, target) {
13334
13481
  const { paths } = await loadVaultConfig(rootDir);
13335
13482
  const graph = await readJsonFile(paths.graphPath);
13336
13483
  const candidate = resolveCandidateTarget(graph?.pages ?? [], target);
13337
- const raw = await fs17.readFile(path21.join(paths.wikiDir, candidate.path), "utf8");
13484
+ const raw = await fs18.readFile(path22.join(paths.wikiDir, candidate.path), "utf8");
13338
13485
  const parsed = matter9(raw);
13339
13486
  const nextUpdatedAt = (/* @__PURE__ */ new Date()).toISOString();
13340
13487
  const nextContent = matter9.stringify(parsed.content, {
@@ -13346,10 +13493,10 @@ async function promoteCandidate(rootDir, target) {
13346
13493
  )
13347
13494
  });
13348
13495
  const nextPath = candidateActivePath(candidate);
13349
- const nextAbsolutePath = path21.join(paths.wikiDir, nextPath);
13350
- await ensureDir(path21.dirname(nextAbsolutePath));
13351
- await fs17.writeFile(nextAbsolutePath, nextContent, "utf8");
13352
- await fs17.rm(path21.join(paths.wikiDir, candidate.path), { force: true });
13496
+ const nextAbsolutePath = path22.join(paths.wikiDir, nextPath);
13497
+ await ensureDir(path22.dirname(nextAbsolutePath));
13498
+ await fs18.writeFile(nextAbsolutePath, nextContent, "utf8");
13499
+ await fs18.rm(path22.join(paths.wikiDir, candidate.path), { force: true });
13353
13500
  const nextPage = parseStoredPage(nextPath, nextContent, { createdAt: candidate.createdAt, updatedAt: nextUpdatedAt });
13354
13501
  const nextPages = sortGraphPages(
13355
13502
  (graph?.pages ?? []).filter((page) => page.id !== candidate.id && page.path !== candidate.path).concat(nextPage)
@@ -13394,7 +13541,7 @@ async function archiveCandidate(rootDir, target) {
13394
13541
  const { paths } = await loadVaultConfig(rootDir);
13395
13542
  const graph = await readJsonFile(paths.graphPath);
13396
13543
  const candidate = resolveCandidateTarget(graph?.pages ?? [], target);
13397
- await fs17.rm(path21.join(paths.wikiDir, candidate.path), { force: true });
13544
+ await fs18.rm(path22.join(paths.wikiDir, candidate.path), { force: true });
13398
13545
  const nextPages = sortGraphPages((graph?.pages ?? []).filter((page) => page.id !== candidate.id && page.path !== candidate.path));
13399
13546
  const nextGraph = {
13400
13547
  generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -13433,18 +13580,18 @@ async function archiveCandidate(rootDir, target) {
13433
13580
  }
13434
13581
  async function ensureObsidianWorkspace(rootDir) {
13435
13582
  const { config } = await loadVaultConfig(rootDir);
13436
- const obsidianDir = path21.join(rootDir, ".obsidian");
13583
+ const obsidianDir = path22.join(rootDir, ".obsidian");
13437
13584
  const projectIds = projectEntries(config).map((project) => project.id);
13438
13585
  await ensureDir(obsidianDir);
13439
13586
  await Promise.all([
13440
- writeJsonFile(path21.join(obsidianDir, "app.json"), {
13587
+ writeJsonFile(path22.join(obsidianDir, "app.json"), {
13441
13588
  alwaysUpdateLinks: true,
13442
13589
  newFileLocation: "folder",
13443
13590
  newFileFolderPath: "wiki/insights",
13444
13591
  useMarkdownLinks: false,
13445
13592
  attachmentFolderPath: "raw/assets"
13446
13593
  }),
13447
- writeJsonFile(path21.join(obsidianDir, "core-plugins.json"), [
13594
+ writeJsonFile(path22.join(obsidianDir, "core-plugins.json"), [
13448
13595
  "file-explorer",
13449
13596
  "global-search",
13450
13597
  "switcher",
@@ -13454,7 +13601,7 @@ async function ensureObsidianWorkspace(rootDir) {
13454
13601
  "tag-pane",
13455
13602
  "page-preview"
13456
13603
  ]),
13457
- writeJsonFile(path21.join(obsidianDir, "graph.json"), {
13604
+ writeJsonFile(path22.join(obsidianDir, "graph.json"), {
13458
13605
  "collapse-filter": false,
13459
13606
  search: "",
13460
13607
  showTags: true,
@@ -13466,7 +13613,7 @@ async function ensureObsidianWorkspace(rootDir) {
13466
13613
  })),
13467
13614
  localJumps: false
13468
13615
  }),
13469
- writeJsonFile(path21.join(obsidianDir, "workspace.json"), {
13616
+ writeJsonFile(path22.join(obsidianDir, "workspace.json"), {
13470
13617
  active: "root",
13471
13618
  lastOpenFiles: ["wiki/index.md", "wiki/projects/index.md", "wiki/candidates/index.md", "wiki/insights/index.md"],
13472
13619
  left: {
@@ -13481,7 +13628,7 @@ async function ensureObsidianWorkspace(rootDir) {
13481
13628
  async function initVault(rootDir, options = {}) {
13482
13629
  const { paths } = await initWorkspace(rootDir);
13483
13630
  await installConfiguredAgents(rootDir);
13484
- const insightsIndexPath = path21.join(paths.wikiDir, "insights", "index.md");
13631
+ const insightsIndexPath = path22.join(paths.wikiDir, "insights", "index.md");
13485
13632
  const now = (/* @__PURE__ */ new Date()).toISOString();
13486
13633
  await writeFileIfChanged(
13487
13634
  insightsIndexPath,
@@ -13517,7 +13664,7 @@ async function initVault(rootDir, options = {}) {
13517
13664
  )
13518
13665
  );
13519
13666
  await writeFileIfChanged(
13520
- path21.join(paths.wikiDir, "projects", "index.md"),
13667
+ path22.join(paths.wikiDir, "projects", "index.md"),
13521
13668
  matter9.stringify(["# Projects", "", "- Run `swarmvault compile` to build project rollups.", ""].join("\n"), {
13522
13669
  page_id: "projects:index",
13523
13670
  kind: "index",
@@ -13539,7 +13686,7 @@ async function initVault(rootDir, options = {}) {
13539
13686
  })
13540
13687
  );
13541
13688
  await writeFileIfChanged(
13542
- path21.join(paths.wikiDir, "candidates", "index.md"),
13689
+ path22.join(paths.wikiDir, "candidates", "index.md"),
13543
13690
  matter9.stringify(["# Candidates", "", "- Run `swarmvault compile` to stage candidate pages.", ""].join("\n"), {
13544
13691
  page_id: "candidates:index",
13545
13692
  kind: "index",
@@ -13678,7 +13825,7 @@ async function compileVault(rootDir, options = {}) {
13678
13825
  ),
13679
13826
  Promise.all(
13680
13827
  clean.map(async (manifest) => {
13681
- const cached = await readJsonFile(path21.join(paths.analysesDir, `${manifest.sourceId}.json`));
13828
+ const cached = await readJsonFile(path22.join(paths.analysesDir, `${manifest.sourceId}.json`));
13682
13829
  if (cached) {
13683
13830
  analysisProgress.tick(manifest.title);
13684
13831
  return cached;
@@ -13706,22 +13853,22 @@ async function compileVault(rootDir, options = {}) {
13706
13853
  }
13707
13854
  const enriched = enrichResolvedCodeImports(manifest, analysis, codeIndex);
13708
13855
  if (analysisSignature(enriched) !== analysisSignature(analysis)) {
13709
- await writeJsonFile(path21.join(paths.analysesDir, `${analysis.sourceId}.json`), enriched);
13856
+ await writeJsonFile(path22.join(paths.analysesDir, `${analysis.sourceId}.json`), enriched);
13710
13857
  }
13711
13858
  return enriched;
13712
13859
  })
13713
13860
  );
13714
13861
  await Promise.all([
13715
- ensureDir(path21.join(paths.wikiDir, "sources")),
13716
- ensureDir(path21.join(paths.wikiDir, "code")),
13717
- ensureDir(path21.join(paths.wikiDir, "concepts")),
13718
- ensureDir(path21.join(paths.wikiDir, "entities")),
13719
- ensureDir(path21.join(paths.wikiDir, "outputs")),
13720
- ensureDir(path21.join(paths.wikiDir, "projects")),
13721
- ensureDir(path21.join(paths.wikiDir, "insights")),
13722
- ensureDir(path21.join(paths.wikiDir, "candidates")),
13723
- ensureDir(path21.join(paths.wikiDir, "candidates", "concepts")),
13724
- ensureDir(path21.join(paths.wikiDir, "candidates", "entities"))
13862
+ ensureDir(path22.join(paths.wikiDir, "sources")),
13863
+ ensureDir(path22.join(paths.wikiDir, "code")),
13864
+ ensureDir(path22.join(paths.wikiDir, "concepts")),
13865
+ ensureDir(path22.join(paths.wikiDir, "entities")),
13866
+ ensureDir(path22.join(paths.wikiDir, "outputs")),
13867
+ ensureDir(path22.join(paths.wikiDir, "projects")),
13868
+ ensureDir(path22.join(paths.wikiDir, "insights")),
13869
+ ensureDir(path22.join(paths.wikiDir, "candidates")),
13870
+ ensureDir(path22.join(paths.wikiDir, "candidates", "concepts")),
13871
+ ensureDir(path22.join(paths.wikiDir, "candidates", "entities"))
13725
13872
  ]);
13726
13873
  const sync = await syncVaultArtifacts(rootDir, {
13727
13874
  schemas,
@@ -13868,7 +14015,7 @@ async function queryVault(rootDir, options) {
13868
14015
  assetFiles: staged.assetFiles
13869
14016
  }
13870
14017
  ]);
13871
- stagedPath = path21.join(approval.approvalDir, "wiki", staged.page.path);
14018
+ stagedPath = path22.join(approval.approvalDir, "wiki", staged.page.path);
13872
14019
  savedPageId = staged.page.id;
13873
14020
  approvalId = approval.approvalId;
13874
14021
  approvalDir = approval.approvalDir;
@@ -14124,9 +14271,9 @@ ${orchestrationNotes.join("\n")}
14124
14271
  approvalId = approval.approvalId;
14125
14272
  approvalDir = approval.approvalDir;
14126
14273
  stepResults.forEach((result, index) => {
14127
- result.stagedPath = path21.join(approval.approvalDir, "wiki", stagedStepPages[index]?.page.path ?? "");
14274
+ result.stagedPath = path22.join(approval.approvalDir, "wiki", stagedStepPages[index]?.page.path ?? "");
14128
14275
  });
14129
- stagedHubPath = path21.join(approval.approvalDir, "wiki", hubPage.path);
14276
+ stagedHubPath = path22.join(approval.approvalDir, "wiki", hubPage.path);
14130
14277
  } else {
14131
14278
  await refreshVaultAfterOutputSave(rootDir);
14132
14279
  }
@@ -14213,11 +14360,11 @@ async function benchmarkVault(rootDir, options = {}) {
14213
14360
  }
14214
14361
  }
14215
14362
  for (const page of graph.pages) {
14216
- const absolutePath = path21.join(paths.wikiDir, page.path);
14363
+ const absolutePath = path22.join(paths.wikiDir, page.path);
14217
14364
  if (!await fileExists(absolutePath)) {
14218
14365
  continue;
14219
14366
  }
14220
- const parsed = matter9(await fs17.readFile(absolutePath, "utf8"));
14367
+ const parsed = matter9(await fs18.readFile(absolutePath, "utf8"));
14221
14368
  pageContentsById.set(page.id, parsed.content);
14222
14369
  }
14223
14370
  const configuredQuestions = (config.benchmark?.questions ?? []).map((question) => normalizeWhitespace(question)).filter(Boolean);
@@ -14262,7 +14409,7 @@ async function listGraphHyperedges(rootDir, target, limit = 25) {
14262
14409
  }
14263
14410
  async function readGraphReport(rootDir) {
14264
14411
  const { paths } = await loadVaultConfig(rootDir);
14265
- return readJsonFile(path21.join(paths.wikiDir, "graph", "report.json"));
14412
+ return readJsonFile(path22.join(paths.wikiDir, "graph", "report.json"));
14266
14413
  }
14267
14414
  async function listGodNodes(rootDir, limit = 10) {
14268
14415
  const graph = await ensureCompiledGraph(rootDir);
@@ -14275,15 +14422,15 @@ async function listPages(rootDir) {
14275
14422
  }
14276
14423
  async function readPage(rootDir, relativePath) {
14277
14424
  const { paths } = await loadVaultConfig(rootDir);
14278
- const absolutePath = path21.resolve(paths.wikiDir, relativePath);
14425
+ const absolutePath = path22.resolve(paths.wikiDir, relativePath);
14279
14426
  if (!absolutePath.startsWith(paths.wikiDir) || !await fileExists(absolutePath)) {
14280
14427
  return null;
14281
14428
  }
14282
- const raw = await fs17.readFile(absolutePath, "utf8");
14429
+ const raw = await fs18.readFile(absolutePath, "utf8");
14283
14430
  const parsed = matter9(raw);
14284
14431
  return {
14285
14432
  path: relativePath,
14286
- title: typeof parsed.data.title === "string" ? parsed.data.title : path21.basename(relativePath, path21.extname(relativePath)),
14433
+ title: typeof parsed.data.title === "string" ? parsed.data.title : path22.basename(relativePath, path22.extname(relativePath)),
14287
14434
  frontmatter: parsed.data,
14288
14435
  content: parsed.content
14289
14436
  };
@@ -14319,7 +14466,7 @@ function structuralLintFindings(_rootDir, paths, graph, schemas, manifests, sour
14319
14466
  severity: "warning",
14320
14467
  code: "stale_page",
14321
14468
  message: `Page ${page.title} is stale because the vault schema changed.`,
14322
- pagePath: path21.join(paths.wikiDir, page.path),
14469
+ pagePath: path22.join(paths.wikiDir, page.path),
14323
14470
  relatedPageIds: [page.id]
14324
14471
  });
14325
14472
  }
@@ -14330,7 +14477,7 @@ function structuralLintFindings(_rootDir, paths, graph, schemas, manifests, sour
14330
14477
  severity: "warning",
14331
14478
  code: "stale_page",
14332
14479
  message: `Page ${page.title} is stale because source ${sourceId} changed.`,
14333
- pagePath: path21.join(paths.wikiDir, page.path),
14480
+ pagePath: path22.join(paths.wikiDir, page.path),
14334
14481
  relatedSourceIds: [sourceId],
14335
14482
  relatedPageIds: [page.id]
14336
14483
  });
@@ -14341,13 +14488,13 @@ function structuralLintFindings(_rootDir, paths, graph, schemas, manifests, sour
14341
14488
  severity: "info",
14342
14489
  code: "orphan_page",
14343
14490
  message: `Page ${page.title} has no backlinks.`,
14344
- pagePath: path21.join(paths.wikiDir, page.path),
14491
+ pagePath: path22.join(paths.wikiDir, page.path),
14345
14492
  relatedPageIds: [page.id]
14346
14493
  });
14347
14494
  }
14348
- const absolutePath = path21.join(paths.wikiDir, page.path);
14495
+ const absolutePath = path22.join(paths.wikiDir, page.path);
14349
14496
  if (await fileExists(absolutePath)) {
14350
- const content = await fs17.readFile(absolutePath, "utf8");
14497
+ const content = await fs18.readFile(absolutePath, "utf8");
14351
14498
  if (content.includes("## Claims")) {
14352
14499
  const uncited = content.split("\n").filter((line) => line.startsWith("- ") && !line.includes("[source:"));
14353
14500
  if (uncited.length) {
@@ -14464,7 +14611,7 @@ async function bootstrapDemo(rootDir, input) {
14464
14611
  }
14465
14612
 
14466
14613
  // src/mcp.ts
14467
- var SERVER_VERSION = "0.1.33";
14614
+ var SERVER_VERSION = "0.2.0";
14468
14615
  async function createMcpServer(rootDir) {
14469
14616
  const server = new McpServer({
14470
14617
  name: "swarmvault",
@@ -14735,7 +14882,7 @@ async function createMcpServer(rootDir) {
14735
14882
  },
14736
14883
  async () => {
14737
14884
  const { paths } = await loadVaultConfig(rootDir);
14738
- const files = (await listFilesRecursive(paths.sessionsDir)).filter((filePath) => filePath.endsWith(".md")).map((filePath) => toPosix(path22.relative(paths.sessionsDir, filePath))).sort();
14885
+ const files = (await listFilesRecursive(paths.sessionsDir)).filter((filePath) => filePath.endsWith(".md")).map((filePath) => toPosix(path23.relative(paths.sessionsDir, filePath))).sort();
14739
14886
  return asTextResource("swarmvault://sessions", JSON.stringify(files, null, 2));
14740
14887
  }
14741
14888
  );
@@ -14768,8 +14915,8 @@ async function createMcpServer(rootDir) {
14768
14915
  return asTextResource(`swarmvault://pages/${encodedPath}`, `Page not found: ${relativePath}`);
14769
14916
  }
14770
14917
  const { paths } = await loadVaultConfig(rootDir);
14771
- const absolutePath = path22.resolve(paths.wikiDir, relativePath);
14772
- return asTextResource(`swarmvault://pages/${encodedPath}`, await fs18.readFile(absolutePath, "utf8"));
14918
+ const absolutePath = path23.resolve(paths.wikiDir, relativePath);
14919
+ return asTextResource(`swarmvault://pages/${encodedPath}`, await fs19.readFile(absolutePath, "utf8"));
14773
14920
  }
14774
14921
  );
14775
14922
  server.registerResource(
@@ -14777,11 +14924,11 @@ async function createMcpServer(rootDir) {
14777
14924
  new ResourceTemplate("swarmvault://sessions/{path}", {
14778
14925
  list: async () => {
14779
14926
  const { paths } = await loadVaultConfig(rootDir);
14780
- const files = (await listFilesRecursive(paths.sessionsDir)).filter((filePath) => filePath.endsWith(".md")).map((filePath) => toPosix(path22.relative(paths.sessionsDir, filePath))).sort();
14927
+ const files = (await listFilesRecursive(paths.sessionsDir)).filter((filePath) => filePath.endsWith(".md")).map((filePath) => toPosix(path23.relative(paths.sessionsDir, filePath))).sort();
14781
14928
  return {
14782
14929
  resources: files.map((relativePath) => ({
14783
14930
  uri: `swarmvault://sessions/${encodeURIComponent(relativePath)}`,
14784
- name: path22.basename(relativePath, ".md"),
14931
+ name: path23.basename(relativePath, ".md"),
14785
14932
  title: relativePath,
14786
14933
  description: "SwarmVault session artifact",
14787
14934
  mimeType: "text/markdown"
@@ -14798,11 +14945,11 @@ async function createMcpServer(rootDir) {
14798
14945
  const { paths } = await loadVaultConfig(rootDir);
14799
14946
  const encodedPath = typeof variables.path === "string" ? variables.path : "";
14800
14947
  const relativePath = decodeURIComponent(encodedPath);
14801
- const absolutePath = path22.resolve(paths.sessionsDir, relativePath);
14948
+ const absolutePath = path23.resolve(paths.sessionsDir, relativePath);
14802
14949
  if (!absolutePath.startsWith(paths.sessionsDir) || !await fileExists(absolutePath)) {
14803
14950
  return asTextResource(`swarmvault://sessions/${encodedPath}`, `Session not found: ${relativePath}`);
14804
14951
  }
14805
- return asTextResource(`swarmvault://sessions/${encodedPath}`, await fs18.readFile(absolutePath, "utf8"));
14952
+ return asTextResource(`swarmvault://sessions/${encodedPath}`, await fs19.readFile(absolutePath, "utf8"));
14806
14953
  }
14807
14954
  );
14808
14955
  return server;
@@ -14850,13 +14997,13 @@ function asTextResource(uri, text) {
14850
14997
  }
14851
14998
 
14852
14999
  // src/schedule.ts
14853
- import fs19 from "fs/promises";
14854
- import path23 from "path";
15000
+ import fs20 from "fs/promises";
15001
+ import path24 from "path";
14855
15002
  function scheduleStatePath(schedulesDir, jobId) {
14856
- return path23.join(schedulesDir, `${encodeURIComponent(jobId)}.json`);
15003
+ return path24.join(schedulesDir, `${encodeURIComponent(jobId)}.json`);
14857
15004
  }
14858
15005
  function scheduleLockPath(schedulesDir, jobId) {
14859
- return path23.join(schedulesDir, `${encodeURIComponent(jobId)}.lock`);
15006
+ return path24.join(schedulesDir, `${encodeURIComponent(jobId)}.lock`);
14860
15007
  }
14861
15008
  function parseEveryDuration(value) {
14862
15009
  const match = value.trim().match(/^(\d+)(m|h|d)$/i);
@@ -14959,13 +15106,13 @@ async function acquireJobLease(rootDir, jobId) {
14959
15106
  const { paths } = await loadVaultConfig(rootDir);
14960
15107
  const leasePath = scheduleLockPath(paths.schedulesDir, jobId);
14961
15108
  await ensureDir(paths.schedulesDir);
14962
- const handle = await fs19.open(leasePath, "wx");
15109
+ const handle = await fs20.open(leasePath, "wx");
14963
15110
  await handle.writeFile(`${process.pid}
14964
15111
  ${(/* @__PURE__ */ new Date()).toISOString()}
14965
15112
  `);
14966
15113
  await handle.close();
14967
15114
  return async () => {
14968
- await fs19.rm(leasePath, { force: true });
15115
+ await fs20.rm(leasePath, { force: true });
14969
15116
  };
14970
15117
  }
14971
15118
  async function listSchedules(rootDir) {
@@ -15111,33 +15258,750 @@ async function serveSchedules(rootDir, pollMs = 3e4) {
15111
15258
  };
15112
15259
  }
15113
15260
 
15261
+ // src/sources.ts
15262
+ import { spawn as spawn2 } from "child_process";
15263
+ import fs21 from "fs/promises";
15264
+ import path25 from "path";
15265
+ import { JSDOM as JSDOM3 } from "jsdom";
15266
+ var DEFAULT_CRAWL_MAX_PAGES = 12;
15267
+ var DEFAULT_CRAWL_MAX_DEPTH = 2;
15268
+ var DOCS_HINT_SEGMENTS = /* @__PURE__ */ new Set([
15269
+ "docs",
15270
+ "documentation",
15271
+ "wiki",
15272
+ "help",
15273
+ "reference",
15274
+ "references",
15275
+ "guide",
15276
+ "guides",
15277
+ "tutorial",
15278
+ "tutorials",
15279
+ "manual",
15280
+ "api",
15281
+ "apis",
15282
+ "getting-started"
15283
+ ]);
15284
+ function uniqueStrings4(values) {
15285
+ return uniqueBy(values.filter(Boolean), (value) => value);
15286
+ }
15287
+ function normalizeManagedStatus(value) {
15288
+ return value === "missing" || value === "error" ? value : "ready";
15289
+ }
15290
+ function withinRoot2(rootPath, targetPath) {
15291
+ const relative = path25.relative(rootPath, targetPath);
15292
+ return relative === "" || !relative.startsWith("..") && !path25.isAbsolute(relative);
15293
+ }
15294
+ async function findNearestGitRoot3(startPath) {
15295
+ let current = path25.resolve(startPath);
15296
+ try {
15297
+ const stat = await fs21.stat(current);
15298
+ if (!stat.isDirectory()) {
15299
+ current = path25.dirname(current);
15300
+ }
15301
+ } catch {
15302
+ current = path25.dirname(current);
15303
+ }
15304
+ while (true) {
15305
+ if (await fileExists(path25.join(current, ".git"))) {
15306
+ return current;
15307
+ }
15308
+ const parent = path25.dirname(current);
15309
+ if (parent === current) {
15310
+ return null;
15311
+ }
15312
+ current = parent;
15313
+ }
15314
+ }
15315
+ function normalizeUrlWithoutHash(input) {
15316
+ const url = new URL(input);
15317
+ url.hash = "";
15318
+ if (url.protocol === "http:" && url.port === "80" || url.protocol === "https:" && url.port === "443") {
15319
+ url.port = "";
15320
+ }
15321
+ return url.toString();
15322
+ }
15323
+ function normalizeGitHubRepoRootUrl(input) {
15324
+ let parsed;
15325
+ try {
15326
+ parsed = new URL(input);
15327
+ } catch {
15328
+ return null;
15329
+ }
15330
+ const host = parsed.hostname.toLowerCase();
15331
+ if (host !== "github.com" && host !== "www.github.com") {
15332
+ return null;
15333
+ }
15334
+ const segments = parsed.pathname.split("/").map((segment) => segment.trim()).filter(Boolean);
15335
+ if (segments.length !== 2) {
15336
+ return null;
15337
+ }
15338
+ const [owner, repoSegment] = segments;
15339
+ const repo = repoSegment.replace(/\.git$/i, "");
15340
+ if (!owner || !repo) {
15341
+ return null;
15342
+ }
15343
+ const url = `https://github.com/${owner}/${repo}`;
15344
+ return {
15345
+ url,
15346
+ cloneUrl: `${url}.git`,
15347
+ title: `${owner}/${repo}`
15348
+ };
15349
+ }
15350
+ function looksLikeDocsPathname(pathname) {
15351
+ const segments = pathname.split("/").map((segment) => segment.trim().toLowerCase()).filter(Boolean);
15352
+ return segments.some((segment) => DOCS_HINT_SEGMENTS.has(segment));
15353
+ }
15354
+ function isLikelyDocsStartUrl(url) {
15355
+ return looksLikeDocsPathname(url.pathname) || url.hostname.toLowerCase().startsWith("docs.");
15356
+ }
15357
+ function normalizeCrawlCandidate(href, baseUrl) {
15358
+ try {
15359
+ const url = new URL(href, baseUrl);
15360
+ if (!["http:", "https:"].includes(url.protocol)) {
15361
+ return null;
15362
+ }
15363
+ if (url.hash) {
15364
+ url.hash = "";
15365
+ }
15366
+ return url.toString();
15367
+ } catch {
15368
+ return null;
15369
+ }
15370
+ }
15371
+ function pathPrefix(pathname) {
15372
+ const segments = pathname.split("/").filter(Boolean);
15373
+ if (segments.length === 0) {
15374
+ return "/";
15375
+ }
15376
+ return `/${segments[0]}`;
15377
+ }
15378
+ function isAllowedDocsCandidate(candidate, startUrl) {
15379
+ if (candidate.origin !== startUrl.origin) {
15380
+ return false;
15381
+ }
15382
+ const extension = path25.extname(candidate.pathname).toLowerCase();
15383
+ if (extension && extension !== ".html" && extension !== ".htm" && extension !== ".md") {
15384
+ return false;
15385
+ }
15386
+ if (looksLikeDocsPathname(candidate.pathname)) {
15387
+ return true;
15388
+ }
15389
+ const startPrefix = pathPrefix(startUrl.pathname);
15390
+ const candidatePrefix = pathPrefix(candidate.pathname);
15391
+ return startPrefix !== "/" && candidatePrefix === startPrefix;
15392
+ }
15393
+ async function fetchHtml(url) {
15394
+ await validateUrlSafety(url);
15395
+ const response = await fetch(url);
15396
+ if (!response.ok) {
15397
+ throw new Error(`Failed to fetch ${url}: ${response.status} ${response.statusText}`);
15398
+ }
15399
+ const contentType = response.headers.get("content-type")?.split(";")[0]?.trim() ?? "text/html";
15400
+ if (!contentType.includes("html")) {
15401
+ throw new Error(`Unsupported docs crawl content type at ${url}: ${contentType}`);
15402
+ }
15403
+ const html = await response.text();
15404
+ const dom = new JSDOM3(html, { url });
15405
+ const document = dom.window.document;
15406
+ const title = document.title.trim() || url;
15407
+ const links = [...document.querySelectorAll("a[href]")].map((anchor) => normalizeCrawlCandidate(anchor.getAttribute("href") ?? "", url)).filter((value) => Boolean(value));
15408
+ return { title, links };
15409
+ }
15410
+ async function crawlDocsSource(url, maxPages, maxDepth) {
15411
+ const startUrl = new URL(normalizeUrlWithoutHash(url));
15412
+ const initial = await fetchHtml(startUrl.toString());
15413
+ const sameDomainDocsLinks = uniqueStrings4(
15414
+ initial.links.filter((candidate) => {
15415
+ const parsed = new URL(candidate);
15416
+ return isAllowedDocsCandidate(parsed, startUrl);
15417
+ })
15418
+ );
15419
+ if (!isLikelyDocsStartUrl(startUrl) && sameDomainDocsLinks.length < 3) {
15420
+ throw new Error(
15421
+ "This URL does not look like a docs hub. Use `swarmvault add` for single articles or `swarmvault ingest` for direct files."
15422
+ );
15423
+ }
15424
+ const visited = /* @__PURE__ */ new Set();
15425
+ const queued = /* @__PURE__ */ new Set();
15426
+ const pages = [];
15427
+ const queue = [{ url: startUrl.toString(), depth: 0 }];
15428
+ queued.add(startUrl.toString());
15429
+ while (queue.length > 0 && pages.length < maxPages) {
15430
+ const current = queue.shift();
15431
+ if (!current) {
15432
+ continue;
15433
+ }
15434
+ if (visited.has(current.url)) {
15435
+ continue;
15436
+ }
15437
+ visited.add(current.url);
15438
+ pages.push(current.url);
15439
+ if (current.depth >= maxDepth) {
15440
+ continue;
15441
+ }
15442
+ const { links } = await fetchHtml(current.url);
15443
+ for (const candidate of links) {
15444
+ if (pages.length + queue.length >= maxPages) {
15445
+ break;
15446
+ }
15447
+ if (queued.has(candidate) || visited.has(candidate)) {
15448
+ continue;
15449
+ }
15450
+ const parsed = new URL(candidate);
15451
+ if (!isAllowedDocsCandidate(parsed, startUrl)) {
15452
+ continue;
15453
+ }
15454
+ queued.add(candidate);
15455
+ queue.push({ url: candidate, depth: current.depth + 1 });
15456
+ }
15457
+ }
15458
+ return {
15459
+ title: initial.title,
15460
+ pages
15461
+ };
15462
+ }
15463
+ function stableManagedSourceId(kind, raw, fallbackTitle) {
15464
+ return `${kind}-${slugify(fallbackTitle)}-${sha256(raw).slice(0, 8)}`;
15465
+ }
15466
+ function matchesManagedSourceSpec(existing, input) {
15467
+ if (existing.kind !== input.kind) {
15468
+ return false;
15469
+ }
15470
+ if (input.kind === "directory") {
15471
+ return path25.resolve(existing.path ?? "") === path25.resolve(input.path);
15472
+ }
15473
+ return (existing.url ?? "") === input.url;
15474
+ }
15475
+ async function resolveManagedSourceInput(rootDir, input) {
15476
+ const absoluteInput = path25.resolve(rootDir, input);
15477
+ if (!(input.startsWith("http://") || input.startsWith("https://"))) {
15478
+ const stat = await fs21.stat(absoluteInput).catch(() => null);
15479
+ if (!stat) {
15480
+ throw new Error(`Source not found: ${input}`);
15481
+ }
15482
+ if (!stat.isDirectory()) {
15483
+ throw new Error(
15484
+ "`swarmvault source add` supports directories, public GitHub repo root URLs, and docs hubs. Use `swarmvault ingest` for single files."
15485
+ );
15486
+ }
15487
+ const detectedRepoRoot = await findNearestGitRoot3(absoluteInput);
15488
+ const repoRoot = detectedRepoRoot && !(withinRoot2(rootDir, absoluteInput) && !withinRoot2(rootDir, detectedRepoRoot)) ? detectedRepoRoot : absoluteInput;
15489
+ return {
15490
+ kind: "directory",
15491
+ path: absoluteInput,
15492
+ repoRoot,
15493
+ title: path25.basename(absoluteInput) || absoluteInput
15494
+ };
15495
+ }
15496
+ const github = normalizeGitHubRepoRootUrl(input);
15497
+ if (github) {
15498
+ return {
15499
+ kind: "github_repo",
15500
+ ...github
15501
+ };
15502
+ }
15503
+ const parsed = new URL(input);
15504
+ if (parsed.hostname.toLowerCase().includes("github.com")) {
15505
+ throw new Error(
15506
+ "`swarmvault source add` only supports public GitHub repo root URLs. Use a repo root like https://github.com/owner/repo."
15507
+ );
15508
+ }
15509
+ return {
15510
+ kind: "crawl_url",
15511
+ url: normalizeUrlWithoutHash(input),
15512
+ title: parsed.hostname
15513
+ };
15514
+ }
15515
+ function directorySourceIdsFor(manifests, inputPath) {
15516
+ return manifests.filter((manifest) => manifest.originalPath && withinRoot2(path25.resolve(inputPath), path25.resolve(manifest.originalPath))).map((manifest) => manifest.sourceId).sort((left, right) => left.localeCompare(right));
15517
+ }
15518
+ async function syncDirectorySource(rootDir, inputPath, repoRoot) {
15519
+ const manifestsBefore = await listManifests(rootDir);
15520
+ const previousInScope = manifestsBefore.filter(
15521
+ (manifest) => manifest.originalPath && withinRoot2(path25.resolve(inputPath), path25.resolve(manifest.originalPath))
15522
+ );
15523
+ const result = await ingestDirectory(rootDir, inputPath, { repoRoot });
15524
+ const removed = [];
15525
+ for (const manifest of previousInScope) {
15526
+ if (!manifest.originalPath) {
15527
+ continue;
15528
+ }
15529
+ if (await fileExists(path25.resolve(manifest.originalPath))) {
15530
+ continue;
15531
+ }
15532
+ const removedManifest = await removeManifestBySourceId(rootDir, manifest.sourceId);
15533
+ if (removedManifest) {
15534
+ removed.push(removedManifest.sourceId);
15535
+ }
15536
+ }
15537
+ const manifestsAfter = await listManifests(rootDir);
15538
+ return {
15539
+ title: path25.basename(inputPath) || inputPath,
15540
+ sourceIds: directorySourceIdsFor(manifestsAfter, inputPath),
15541
+ counts: {
15542
+ scannedCount: result.scannedCount,
15543
+ importedCount: result.imported.length,
15544
+ updatedCount: result.updated.length,
15545
+ removedCount: removed.length,
15546
+ skippedCount: result.skipped.length
15547
+ },
15548
+ changed: result.imported.length + result.updated.length + removed.length > 0
15549
+ };
15550
+ }
15551
+ async function runGitCommand(cwd, args) {
15552
+ await new Promise((resolve, reject) => {
15553
+ const child = spawn2("git", args, {
15554
+ cwd,
15555
+ stdio: ["ignore", "pipe", "pipe"]
15556
+ });
15557
+ let stderr = "";
15558
+ child.stderr.on("data", (chunk) => {
15559
+ stderr += chunk.toString("utf8");
15560
+ });
15561
+ child.on("error", reject);
15562
+ child.on("close", (code) => {
15563
+ if (code === 0) {
15564
+ resolve();
15565
+ return;
15566
+ }
15567
+ reject(new Error(stderr.trim() || `git ${args.join(" ")} failed with exit code ${code ?? 1}`));
15568
+ });
15569
+ });
15570
+ }
15571
+ async function syncGitHubRepoSource(rootDir, entry) {
15572
+ const workingDir = await managedSourceWorkingDir(rootDir, entry.id);
15573
+ const checkoutDir = path25.join(workingDir, "checkout");
15574
+ await fs21.rm(checkoutDir, { recursive: true, force: true });
15575
+ await ensureDir(workingDir);
15576
+ if (!entry.url) {
15577
+ throw new Error(`Managed source ${entry.id} is missing its repository URL.`);
15578
+ }
15579
+ const github = normalizeGitHubRepoRootUrl(entry.url);
15580
+ if (!github) {
15581
+ throw new Error(`Managed source ${entry.id} has an invalid GitHub repo URL.`);
15582
+ }
15583
+ await runGitCommand(workingDir, ["clone", "--depth", "1", github.cloneUrl, "checkout"]);
15584
+ return await syncDirectorySource(rootDir, checkoutDir, checkoutDir);
15585
+ }
15586
+ async function syncCrawlSource(rootDir, entry, options) {
15587
+ if (!entry.url) {
15588
+ throw new Error(`Managed source ${entry.id} is missing its URL.`);
15589
+ }
15590
+ const crawl = await crawlDocsSource(entry.url, options.maxPages ?? DEFAULT_CRAWL_MAX_PAGES, options.maxDepth ?? DEFAULT_CRAWL_MAX_DEPTH);
15591
+ const previousSourceIds = [...entry.sourceIds];
15592
+ const currentSourceIds = [];
15593
+ let importedCount = 0;
15594
+ let updatedCount = 0;
15595
+ for (const pageUrl of crawl.pages) {
15596
+ const persisted = await ingestInputDetailed(rootDir, pageUrl);
15597
+ currentSourceIds.push(persisted.manifest.sourceId);
15598
+ if (persisted.isNew) {
15599
+ importedCount += 1;
15600
+ } else if (persisted.wasUpdated) {
15601
+ updatedCount += 1;
15602
+ }
15603
+ }
15604
+ let removedCount = 0;
15605
+ for (const sourceId of previousSourceIds) {
15606
+ if (currentSourceIds.includes(sourceId)) {
15607
+ continue;
15608
+ }
15609
+ if (await removeManifestBySourceId(rootDir, sourceId)) {
15610
+ removedCount += 1;
15611
+ }
15612
+ }
15613
+ return {
15614
+ title: crawl.title,
15615
+ sourceIds: uniqueStrings4(currentSourceIds).sort((left, right) => left.localeCompare(right)),
15616
+ counts: {
15617
+ scannedCount: crawl.pages.length,
15618
+ importedCount,
15619
+ updatedCount,
15620
+ removedCount,
15621
+ skippedCount: 0
15622
+ },
15623
+ changed: importedCount + updatedCount + removedCount > 0
15624
+ };
15625
+ }
15626
+ async function syncManagedSource(rootDir, entry, options) {
15627
+ const now = (/* @__PURE__ */ new Date()).toISOString();
15628
+ try {
15629
+ let sync;
15630
+ if (entry.kind === "directory") {
15631
+ if (!entry.path || !entry.repoRoot) {
15632
+ throw new Error(`Managed source ${entry.id} is missing its directory path.`);
15633
+ }
15634
+ if (!await fileExists(entry.path)) {
15635
+ return {
15636
+ ...entry,
15637
+ status: "missing",
15638
+ updatedAt: now,
15639
+ lastSyncAt: now,
15640
+ lastSyncStatus: "error",
15641
+ lastError: `Directory not found: ${entry.path}`,
15642
+ changed: false
15643
+ };
15644
+ }
15645
+ sync = await syncDirectorySource(rootDir, entry.path, entry.repoRoot);
15646
+ } else if (entry.kind === "github_repo") {
15647
+ sync = await syncGitHubRepoSource(rootDir, entry);
15648
+ } else {
15649
+ sync = await syncCrawlSource(rootDir, entry, options);
15650
+ }
15651
+ return {
15652
+ ...entry,
15653
+ title: sync.title || entry.title,
15654
+ sourceIds: sync.sourceIds,
15655
+ status: "ready",
15656
+ updatedAt: now,
15657
+ lastSyncAt: now,
15658
+ lastSyncStatus: "success",
15659
+ lastSyncCounts: sync.counts,
15660
+ lastError: void 0,
15661
+ changed: sync.changed
15662
+ };
15663
+ } catch (error) {
15664
+ return {
15665
+ ...entry,
15666
+ status: normalizeManagedStatus(entry.status),
15667
+ updatedAt: now,
15668
+ lastSyncAt: now,
15669
+ lastSyncStatus: "error",
15670
+ lastError: error instanceof Error ? error.message : String(error),
15671
+ changed: false
15672
+ };
15673
+ }
15674
+ }
15675
+ function scopedSourcePages(graph, sourceIds) {
15676
+ const scopedSet = new Set(sourceIds);
15677
+ return graph.pages.filter((page) => page.sourceIds.some((sourceId) => scopedSet.has(sourceId)));
15678
+ }
15679
+ function scopedNodeIds(graph, sourceIds) {
15680
+ const scopedSet = new Set(sourceIds);
15681
+ return graph.nodes.filter((node) => node.sourceIds.some((sourceId) => scopedSet.has(sourceId))).map((node) => node.id);
15682
+ }
15683
+ async function loadSourceAnalyses(rootDir, sourceIds) {
15684
+ const { paths } = await loadVaultConfig(rootDir);
15685
+ const analyses = await Promise.all(
15686
+ sourceIds.map(async (sourceId) => await readJsonFile(path25.join(paths.analysesDir, `${sourceId}.json`)))
15687
+ );
15688
+ return analyses.filter((analysis) => Boolean(analysis?.sourceId));
15689
+ }
15690
+ function renderDeterministicSourceBrief(input) {
15691
+ const modulePages = input.sourcePages.filter((page) => page.kind === "module").slice(0, 6);
15692
+ const sourcePages = input.sourcePages.filter((page) => page.kind === "source").slice(0, 6);
15693
+ const conceptPages = input.sourcePages.filter((page) => page.kind === "concept").slice(0, 6);
15694
+ const entityPages = input.sourcePages.filter((page) => page.kind === "entity").slice(0, 6);
15695
+ const questions = uniqueStrings4(input.analyses.flatMap((analysis) => analysis.questions)).slice(0, 5);
15696
+ const summary = truncate(
15697
+ normalizeWhitespace(
15698
+ uniqueStrings4(input.analyses.map((analysis) => analysis.summary).filter(Boolean)).join(" ") || `${input.source.title} has been compiled into a local source graph.`
15699
+ ),
15700
+ 320
15701
+ );
15702
+ const scopedNodeIdSet = new Set(scopedNodeIds(input.graph, input.source.sourceIds));
15703
+ const surprises = input.report?.surprisingConnections.filter((connection) => scopedNodeIdSet.has(connection.sourceNodeId) || scopedNodeIdSet.has(connection.targetNodeId)).slice(0, 4) ?? [];
15704
+ const contradictions = input.report?.contradictions.filter(
15705
+ (contradiction) => input.source.sourceIds.includes(contradiction.sourceIdA) || input.source.sourceIds.includes(contradiction.sourceIdB)
15706
+ ) ?? [];
15707
+ return [
15708
+ `# Source Brief: ${input.source.title}`,
15709
+ "",
15710
+ "## What This Source Is",
15711
+ "",
15712
+ summary,
15713
+ "",
15714
+ "## Read First",
15715
+ "",
15716
+ ...sourcePages.length ? sourcePages.map((page) => `- [[${page.path.replace(/\.md$/, "")}|${page.title}]]`) : ["- No source page links are available yet."],
15717
+ "",
15718
+ "## Core Pages",
15719
+ "",
15720
+ ...modulePages.length ? modulePages.map((page) => `- [[${page.path.replace(/\.md$/, "")}|${page.title}]]`) : ["- No module pages are available yet."],
15721
+ ...conceptPages.length ? ["", "Concept pages:", ...conceptPages.map((page) => `- [[${page.path.replace(/\.md$/, "")}|${page.title}]]`)] : [],
15722
+ ...entityPages.length ? ["", "Entity pages:", ...entityPages.map((page) => `- [[${page.path.replace(/\.md$/, "")}|${page.title}]]`)] : [],
15723
+ "",
15724
+ "## How The Important Parts Fit Together",
15725
+ "",
15726
+ `- Compiled source pages: ${sourcePages.length}`,
15727
+ `- Module pages: ${modulePages.length}`,
15728
+ `- Graph nodes touching this source: ${scopedNodeIdSet.size}`,
15729
+ `- Current tracked source ids: ${input.source.sourceIds.length}`,
15730
+ "",
15731
+ "## Surprises",
15732
+ "",
15733
+ ...surprises.length ? surprises.map((surprise) => `- ${surprise.explanation}`) : ["- No surprising cross-source connections were highlighted for this source yet."],
15734
+ "",
15735
+ "## Contradictions",
15736
+ "",
15737
+ ...contradictions.length ? contradictions.map(
15738
+ (contradiction) => `- ${contradiction.claimA} / ${contradiction.claimB} (sources: ${contradiction.sourceIdA}, ${contradiction.sourceIdB})`
15739
+ ) : ["- No contradictions were detected for this source."],
15740
+ "",
15741
+ "## Open Questions",
15742
+ "",
15743
+ ...questions.length ? questions.map((question) => `- ${question}`) : ["- No extracted open questions yet."],
15744
+ "",
15745
+ "## Suggested Next Questions",
15746
+ "",
15747
+ ...(input.report?.suggestedQuestions ?? []).slice(0, 5).map((question) => `- ${question}`) || [
15748
+ "- Ask `swarmvault query` about the main modules or sections in this source."
15749
+ ],
15750
+ ""
15751
+ ].join("\n");
15752
+ }
15753
+ async function generateSourceBriefMarkdown(rootDir, source) {
15754
+ const { paths } = await loadVaultConfig(rootDir);
15755
+ const graph = await readJsonFile(paths.graphPath);
15756
+ if (!graph) {
15757
+ return null;
15758
+ }
15759
+ const sourcePages = scopedSourcePages(graph, source.sourceIds);
15760
+ const analyses = await loadSourceAnalyses(rootDir, source.sourceIds);
15761
+ const report = await readGraphReport(rootDir);
15762
+ const fallback = renderDeterministicSourceBrief({
15763
+ source,
15764
+ sourcePages,
15765
+ graph,
15766
+ analyses,
15767
+ report
15768
+ });
15769
+ const provider = await getProviderForTask(rootDir, "queryProvider");
15770
+ if (provider.type === "heuristic") {
15771
+ return fallback;
15772
+ }
15773
+ try {
15774
+ const schemas = await loadVaultSchemas(rootDir);
15775
+ const pageContext = sourcePages.slice(0, 10).map((page) => `- ${page.title} (${page.kind}) -> ${page.path}`).join("\n");
15776
+ const analysisContext = analyses.slice(0, 6).map(
15777
+ (analysis) => `# ${analysis.title}
15778
+ Summary: ${analysis.summary}
15779
+ Questions: ${analysis.questions.join(" | ") || "none"}
15780
+ Concepts: ${analysis.concepts.map((concept) => concept.name).join(", ") || "none"}
15781
+ Entities: ${analysis.entities.map((entity) => entity.name).join(", ") || "none"}`
15782
+ ).join("\n\n---\n\n");
15783
+ const response = await provider.generateText({
15784
+ system: buildSchemaPrompt(
15785
+ schemas.effective.global,
15786
+ "Write a concise markdown source brief with sections: What This Source Is, Read First, Core Pages, How The Important Parts Fit Together, Surprises, Contradictions, Open Questions, Suggested Next Questions. Ground every claim in the provided context."
15787
+ ),
15788
+ prompt: [
15789
+ `Source title: ${source.title}`,
15790
+ `Source kind: ${source.kind}`,
15791
+ `Tracked source ids: ${source.sourceIds.join(", ") || "none"}`,
15792
+ "",
15793
+ "Pages:",
15794
+ pageContext || "- none",
15795
+ "",
15796
+ "Analyses:",
15797
+ analysisContext || "No analysis context available.",
15798
+ "",
15799
+ "Deterministic fallback draft:",
15800
+ fallback
15801
+ ].join("\n")
15802
+ });
15803
+ return response.text?.trim() ? response.text.trim() : fallback;
15804
+ } catch {
15805
+ return fallback;
15806
+ }
15807
+ }
15808
+ async function writeSourceBrief(rootDir, source) {
15809
+ if (!source.sourceIds.length) {
15810
+ return null;
15811
+ }
15812
+ const { paths } = await loadVaultConfig(rootDir);
15813
+ const markdown = await generateSourceBriefMarkdown(rootDir, source);
15814
+ if (!markdown) {
15815
+ return null;
15816
+ }
15817
+ const graph = await readJsonFile(paths.graphPath);
15818
+ const relatedPages = graph ? scopedSourcePages(graph, source.sourceIds) : [];
15819
+ const relatedPageIds = relatedPages.slice(0, 12).map((page) => page.id);
15820
+ const relatedNodeIds = graph ? scopedNodeIds(graph, source.sourceIds).slice(0, 20) : [];
15821
+ const projectIds = uniqueStrings4(relatedPages.flatMap((page) => page.projectIds));
15822
+ const now = (/* @__PURE__ */ new Date()).toISOString();
15823
+ const output = buildOutputPage({
15824
+ title: `Source Brief: ${source.title}`,
15825
+ question: `Brief ${source.title}`,
15826
+ answer: markdown,
15827
+ citations: source.sourceIds,
15828
+ schemaHash: graph?.generatedAt ?? "",
15829
+ outputFormat: "report",
15830
+ relatedPageIds,
15831
+ relatedNodeIds,
15832
+ relatedSourceIds: source.sourceIds,
15833
+ projectIds,
15834
+ extraTags: ["source-brief"],
15835
+ origin: "query",
15836
+ slug: `source-briefs/${source.id}`,
15837
+ metadata: {
15838
+ status: "active",
15839
+ createdAt: now,
15840
+ updatedAt: now,
15841
+ compiledFrom: source.sourceIds,
15842
+ managedBy: "system",
15843
+ confidence: 0.82
15844
+ }
15845
+ });
15846
+ const absolutePath = path25.join(paths.wikiDir, output.page.path);
15847
+ await ensureDir(path25.dirname(absolutePath));
15848
+ await fs21.writeFile(absolutePath, output.content, "utf8");
15849
+ return absolutePath;
15850
+ }
15851
+ async function generateBriefsForSources(rootDir, sources) {
15852
+ const briefPaths = /* @__PURE__ */ new Map();
15853
+ for (const source of sources) {
15854
+ const briefPath = await writeSourceBrief(rootDir, source);
15855
+ if (briefPath) {
15856
+ briefPaths.set(source.id, briefPath);
15857
+ }
15858
+ }
15859
+ if (briefPaths.size > 0) {
15860
+ await refreshVaultAfterOutputSave(rootDir);
15861
+ }
15862
+ return briefPaths;
15863
+ }
15864
+ function shouldCompile(changedSources, graphExists, compileRequested) {
15865
+ return compileRequested && (!graphExists || changedSources.length > 0);
15866
+ }
15867
+ async function listManagedSourceRecords(rootDir) {
15868
+ await ensureManagedSourcesArtifact(rootDir);
15869
+ return await loadManagedSources(rootDir);
15870
+ }
15871
+ async function addManagedSource(rootDir, input, options = {}) {
15872
+ const compileRequested = options.compile ?? true;
15873
+ const briefRequested = options.brief ?? true;
15874
+ const sources = await loadManagedSources(rootDir);
15875
+ const resolved = await resolveManagedSourceInput(rootDir, input);
15876
+ const existing = sources.find((candidate) => matchesManagedSourceSpec(candidate, resolved));
15877
+ const now = (/* @__PURE__ */ new Date()).toISOString();
15878
+ const source = existing ?? {
15879
+ id: resolved.kind === "directory" ? stableManagedSourceId("directory", path25.resolve(resolved.path), resolved.title) : stableManagedSourceId(resolved.kind, resolved.url, resolved.title),
15880
+ kind: resolved.kind,
15881
+ title: resolved.title,
15882
+ path: resolved.kind === "directory" ? resolved.path : void 0,
15883
+ repoRoot: resolved.kind === "directory" ? resolved.repoRoot : void 0,
15884
+ url: resolved.kind === "directory" ? void 0 : resolved.url,
15885
+ createdAt: now,
15886
+ updatedAt: now,
15887
+ status: "ready",
15888
+ sourceIds: []
15889
+ };
15890
+ const synced = await syncManagedSource(rootDir, source, options);
15891
+ if (synced.lastSyncStatus === "error") {
15892
+ throw new Error(synced.lastError ?? `Failed to add managed source ${synced.id}.`);
15893
+ }
15894
+ const graphExists = await loadVaultConfig(rootDir).then(({ paths }) => fileExists(paths.graphPath));
15895
+ let compile;
15896
+ if (shouldCompile([synced], graphExists, compileRequested)) {
15897
+ compile = await compileVault(rootDir, {});
15898
+ }
15899
+ let briefGenerated = false;
15900
+ let briefPath;
15901
+ if (compileRequested && briefRequested && synced.status === "ready") {
15902
+ const briefs = await generateBriefsForSources(rootDir, [synced]);
15903
+ briefPath = briefs.get(synced.id);
15904
+ briefGenerated = Boolean(briefPath);
15905
+ }
15906
+ const nextSource = {
15907
+ ...synced,
15908
+ briefPath: briefPath ?? synced.briefPath,
15909
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
15910
+ };
15911
+ const nextSources = existing ? sources.map((candidate) => candidate.id === nextSource.id ? nextSource : candidate) : [...sources, nextSource];
15912
+ await saveManagedSources(rootDir, nextSources);
15913
+ return {
15914
+ source: nextSource,
15915
+ compile,
15916
+ briefGenerated
15917
+ };
15918
+ }
15919
+ async function reloadManagedSources(rootDir, options = {}) {
15920
+ const compileRequested = options.compile ?? true;
15921
+ const briefRequested = options.brief ?? true;
15922
+ const sources = await loadManagedSources(rootDir);
15923
+ const selected = options.all || !options.id ? sources : sources.filter((source) => source.id === options.id);
15924
+ if (!selected.length) {
15925
+ throw new Error(options.id ? `Managed source not found: ${options.id}` : "No managed sources registered.");
15926
+ }
15927
+ const syncedSources = [];
15928
+ const changedSources = [];
15929
+ for (const source of selected) {
15930
+ const synced = await syncManagedSource(rootDir, source, options);
15931
+ syncedSources.push(synced);
15932
+ if (synced.changed) {
15933
+ changedSources.push(synced);
15934
+ }
15935
+ }
15936
+ const graphExists = await loadVaultConfig(rootDir).then(({ paths }) => fileExists(paths.graphPath));
15937
+ let compile;
15938
+ if (shouldCompile(changedSources, graphExists, compileRequested)) {
15939
+ compile = await compileVault(rootDir, {});
15940
+ }
15941
+ const briefPaths = compileRequested && briefRequested ? await generateBriefsForSources(
15942
+ rootDir,
15943
+ syncedSources.filter((source) => source.status === "ready")
15944
+ ) : /* @__PURE__ */ new Map();
15945
+ const nextSources = sources.map((source) => {
15946
+ const synced = syncedSources.find((candidate) => candidate.id === source.id);
15947
+ if (!synced) {
15948
+ return source;
15949
+ }
15950
+ return {
15951
+ ...synced,
15952
+ briefPath: briefPaths.get(synced.id) ?? synced.briefPath,
15953
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
15954
+ };
15955
+ });
15956
+ await saveManagedSources(rootDir, nextSources);
15957
+ return {
15958
+ sources: nextSources.filter((source) => selected.some((candidate) => candidate.id === source.id)),
15959
+ compile,
15960
+ briefPaths: [...briefPaths.values()]
15961
+ };
15962
+ }
15963
+ async function deleteManagedSource(rootDir, id) {
15964
+ const sources = await loadManagedSources(rootDir);
15965
+ const target = sources.find((source) => source.id === id);
15966
+ if (!target) {
15967
+ throw new Error(`Managed source not found: ${id}`);
15968
+ }
15969
+ await saveManagedSources(
15970
+ rootDir,
15971
+ sources.filter((source) => source.id !== id)
15972
+ );
15973
+ const workingDir = await managedSourceWorkingDir(rootDir, id);
15974
+ await fs21.rm(workingDir, { recursive: true, force: true });
15975
+ return { removed: target };
15976
+ }
15977
+
15114
15978
  // src/viewer.ts
15115
15979
  import { execFile } from "child_process";
15116
- import fs20 from "fs/promises";
15980
+ import fs22 from "fs/promises";
15117
15981
  import http from "http";
15118
- import path25 from "path";
15982
+ import path27 from "path";
15119
15983
  import { promisify } from "util";
15120
15984
  import matter10 from "gray-matter";
15121
15985
  import mime2 from "mime-types";
15122
15986
 
15123
15987
  // src/watch.ts
15124
- import path24 from "path";
15988
+ import path26 from "path";
15125
15989
  import process3 from "process";
15126
15990
  import chokidar from "chokidar";
15127
15991
  var MAX_BACKOFF_MS = 3e4;
15128
15992
  var BACKOFF_THRESHOLD = 3;
15129
15993
  var CRITICAL_THRESHOLD = 10;
15130
15994
  var REPO_WATCH_IGNORES = /* @__PURE__ */ new Set([".git", ".venv"]);
15131
- function withinRoot2(rootPath, targetPath) {
15132
- const relative = path24.relative(rootPath, targetPath);
15133
- return relative === "" || !relative.startsWith("..") && !path24.isAbsolute(relative);
15995
+ function withinRoot3(rootPath, targetPath) {
15996
+ const relative = path26.relative(rootPath, targetPath);
15997
+ return relative === "" || !relative.startsWith("..") && !path26.isAbsolute(relative);
15134
15998
  }
15135
15999
  function hasIgnoredRepoSegment(baseDir, targetPath) {
15136
- const relativePath = path24.relative(baseDir, targetPath);
16000
+ const relativePath = path26.relative(baseDir, targetPath);
15137
16001
  if (!relativePath || relativePath.startsWith("..")) {
15138
16002
  return false;
15139
16003
  }
15140
- return relativePath.split(path24.sep).some((segment) => REPO_WATCH_IGNORES.has(segment));
16004
+ return relativePath.split(path26.sep).some((segment) => REPO_WATCH_IGNORES.has(segment));
15141
16005
  }
15142
16006
  function workspaceIgnoreRoots(rootDir, paths) {
15143
16007
  return [
@@ -15146,16 +16010,16 @@ function workspaceIgnoreRoots(rootDir, paths) {
15146
16010
  paths.stateDir,
15147
16011
  paths.agentDir,
15148
16012
  paths.inboxDir,
15149
- path24.join(rootDir, ".claude"),
15150
- path24.join(rootDir, ".cursor"),
15151
- path24.join(rootDir, ".obsidian")
15152
- ].map((candidate) => path24.resolve(candidate));
16013
+ path26.join(rootDir, ".claude"),
16014
+ path26.join(rootDir, ".cursor"),
16015
+ path26.join(rootDir, ".obsidian")
16016
+ ].map((candidate) => path26.resolve(candidate));
15153
16017
  }
15154
16018
  async function resolveWatchTargets(rootDir, paths, options) {
15155
- const targets = /* @__PURE__ */ new Set([path24.resolve(paths.inboxDir)]);
16019
+ const targets = /* @__PURE__ */ new Set([path26.resolve(paths.inboxDir)]);
15156
16020
  if (options.repo) {
15157
16021
  for (const repoRoot of await listTrackedRepoRoots(rootDir)) {
15158
- targets.add(path24.resolve(repoRoot));
16022
+ targets.add(path26.resolve(repoRoot));
15159
16023
  }
15160
16024
  }
15161
16025
  return [...targets].sort((left, right) => left.localeCompare(right));
@@ -15285,7 +16149,7 @@ async function watchVault(rootDir, options = {}) {
15285
16149
  const { paths } = await initWorkspace(rootDir);
15286
16150
  const baseDebounceMs = options.debounceMs ?? 900;
15287
16151
  const ignoredRoots = workspaceIgnoreRoots(rootDir, paths);
15288
- const inboxWatchRoot = path24.resolve(paths.inboxDir);
16152
+ const inboxWatchRoot = path26.resolve(paths.inboxDir);
15289
16153
  let watchTargets = await resolveWatchTargets(rootDir, paths, options);
15290
16154
  let timer;
15291
16155
  let running = false;
@@ -15300,12 +16164,12 @@ async function watchVault(rootDir, options = {}) {
15300
16164
  usePolling: true,
15301
16165
  interval: 100,
15302
16166
  ignored: (targetPath) => {
15303
- const absolutePath = path24.resolve(targetPath);
15304
- const primaryTarget = watchTargets.filter((watchTarget) => withinRoot2(watchTarget, absolutePath)).sort((left, right) => right.length - left.length)[0] ?? null;
16167
+ const absolutePath = path26.resolve(targetPath);
16168
+ const primaryTarget = watchTargets.filter((watchTarget) => withinRoot3(watchTarget, absolutePath)).sort((left, right) => right.length - left.length)[0] ?? null;
15305
16169
  if (!primaryTarget) {
15306
16170
  return false;
15307
16171
  }
15308
- if (primaryTarget !== inboxWatchRoot && ignoredRoots.some((ignoreRoot) => withinRoot2(ignoreRoot, absolutePath))) {
16172
+ if (primaryTarget !== inboxWatchRoot && ignoredRoots.some((ignoreRoot) => withinRoot3(ignoreRoot, absolutePath))) {
15309
16173
  return true;
15310
16174
  }
15311
16175
  return hasIgnoredRepoSegment(primaryTarget, absolutePath);
@@ -15489,8 +16353,8 @@ async function watchVault(rootDir, options = {}) {
15489
16353
  }
15490
16354
  };
15491
16355
  const reasonForPath = (targetPath) => {
15492
- const baseDir = watchTargets.filter((watchTarget) => withinRoot2(watchTarget, path24.resolve(targetPath))).sort((left, right) => right.length - left.length)[0] ?? paths.inboxDir;
15493
- return path24.relative(baseDir, targetPath) || ".";
16356
+ const baseDir = watchTargets.filter((watchTarget) => withinRoot3(watchTarget, path26.resolve(targetPath))).sort((left, right) => right.length - left.length)[0] ?? paths.inboxDir;
16357
+ return path26.relative(baseDir, targetPath) || ".";
15494
16358
  };
15495
16359
  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)}`));
15496
16360
  await new Promise((resolve, reject) => {
@@ -15532,15 +16396,15 @@ async function getWatchStatus(rootDir) {
15532
16396
  var execFileAsync = promisify(execFile);
15533
16397
  async function readViewerPage(rootDir, relativePath) {
15534
16398
  const { paths } = await loadVaultConfig(rootDir);
15535
- const absolutePath = path25.resolve(paths.wikiDir, relativePath);
16399
+ const absolutePath = path27.resolve(paths.wikiDir, relativePath);
15536
16400
  if (!absolutePath.startsWith(paths.wikiDir) || !await fileExists(absolutePath)) {
15537
16401
  return null;
15538
16402
  }
15539
- const raw = await fs20.readFile(absolutePath, "utf8");
16403
+ const raw = await fs22.readFile(absolutePath, "utf8");
15540
16404
  const parsed = matter10(raw);
15541
16405
  return {
15542
16406
  path: relativePath,
15543
- title: typeof parsed.data.title === "string" ? parsed.data.title : path25.basename(relativePath, path25.extname(relativePath)),
16407
+ title: typeof parsed.data.title === "string" ? parsed.data.title : path27.basename(relativePath, path27.extname(relativePath)),
15544
16408
  frontmatter: parsed.data,
15545
16409
  content: parsed.content,
15546
16410
  assets: normalizeOutputAssets(parsed.data.output_assets)
@@ -15548,12 +16412,12 @@ async function readViewerPage(rootDir, relativePath) {
15548
16412
  }
15549
16413
  async function readViewerAsset(rootDir, relativePath) {
15550
16414
  const { paths } = await loadVaultConfig(rootDir);
15551
- const absolutePath = path25.resolve(paths.wikiDir, relativePath);
16415
+ const absolutePath = path27.resolve(paths.wikiDir, relativePath);
15552
16416
  if (!absolutePath.startsWith(paths.wikiDir) || !await fileExists(absolutePath)) {
15553
16417
  return null;
15554
16418
  }
15555
16419
  return {
15556
- buffer: await fs20.readFile(absolutePath),
16420
+ buffer: await fs22.readFile(absolutePath),
15557
16421
  mimeType: mime2.lookup(absolutePath) || "application/octet-stream"
15558
16422
  };
15559
16423
  }
@@ -15576,12 +16440,12 @@ async function readJsonBody(request) {
15576
16440
  return JSON.parse(raw);
15577
16441
  }
15578
16442
  async function ensureViewerDist(viewerDistDir) {
15579
- const indexPath = path25.join(viewerDistDir, "index.html");
16443
+ const indexPath = path27.join(viewerDistDir, "index.html");
15580
16444
  if (await fileExists(indexPath)) {
15581
16445
  return;
15582
16446
  }
15583
- const viewerProjectDir = path25.dirname(viewerDistDir);
15584
- if (await fileExists(path25.join(viewerProjectDir, "package.json"))) {
16447
+ const viewerProjectDir = path27.dirname(viewerDistDir);
16448
+ if (await fileExists(path27.join(viewerProjectDir, "package.json"))) {
15585
16449
  await execFileAsync("pnpm", ["build"], { cwd: viewerProjectDir });
15586
16450
  }
15587
16451
  }
@@ -15598,7 +16462,7 @@ async function startGraphServer(rootDir, port) {
15598
16462
  return;
15599
16463
  }
15600
16464
  response.writeHead(200, { "content-type": "application/json" });
15601
- response.end(await fs20.readFile(paths.graphPath, "utf8"));
16465
+ response.end(await fs22.readFile(paths.graphPath, "utf8"));
15602
16466
  return;
15603
16467
  }
15604
16468
  if (url.pathname === "/api/graph/query") {
@@ -15655,14 +16519,14 @@ async function startGraphServer(rootDir, port) {
15655
16519
  return;
15656
16520
  }
15657
16521
  if (url.pathname === "/api/graph-report") {
15658
- const reportPath = path25.join(paths.wikiDir, "graph", "report.json");
16522
+ const reportPath = path27.join(paths.wikiDir, "graph", "report.json");
15659
16523
  if (!await fileExists(reportPath)) {
15660
16524
  response.writeHead(404, { "content-type": "application/json" });
15661
16525
  response.end(JSON.stringify({ error: "Graph report artifact not found. Run `swarmvault compile` first." }));
15662
16526
  return;
15663
16527
  }
15664
16528
  response.writeHead(200, { "content-type": "application/json" });
15665
- response.end(await fs20.readFile(reportPath, "utf8"));
16529
+ response.end(await fs22.readFile(reportPath, "utf8"));
15666
16530
  return;
15667
16531
  }
15668
16532
  if (url.pathname === "/api/watch-status") {
@@ -15745,8 +16609,8 @@ async function startGraphServer(rootDir, port) {
15745
16609
  return;
15746
16610
  }
15747
16611
  const relativePath = url.pathname === "/" ? "index.html" : url.pathname.slice(1);
15748
- const target = path25.join(paths.viewerDistDir, relativePath);
15749
- const fallback = path25.join(paths.viewerDistDir, "index.html");
16612
+ const target = path27.join(paths.viewerDistDir, relativePath);
16613
+ const fallback = path27.join(paths.viewerDistDir, "index.html");
15750
16614
  const filePath = await fileExists(target) ? target : fallback;
15751
16615
  if (!await fileExists(filePath)) {
15752
16616
  response.writeHead(503, { "content-type": "text/plain" });
@@ -15754,7 +16618,7 @@ async function startGraphServer(rootDir, port) {
15754
16618
  return;
15755
16619
  }
15756
16620
  response.writeHead(200, { "content-type": mime2.lookup(filePath) || "text/plain" });
15757
- response.end(await fs20.readFile(filePath));
16621
+ response.end(await fs22.readFile(filePath));
15758
16622
  });
15759
16623
  await new Promise((resolve) => {
15760
16624
  server.listen(effectivePort, resolve);
@@ -15781,7 +16645,7 @@ async function exportGraphHtml(rootDir, outputPath) {
15781
16645
  throw new Error("Graph artifact not found. Run `swarmvault compile` first.");
15782
16646
  }
15783
16647
  await ensureViewerDist(paths.viewerDistDir);
15784
- const indexPath = path25.join(paths.viewerDistDir, "index.html");
16648
+ const indexPath = path27.join(paths.viewerDistDir, "index.html");
15785
16649
  if (!await fileExists(indexPath)) {
15786
16650
  throw new Error("Viewer build not found. Run `pnpm build` first.");
15787
16651
  }
@@ -15807,17 +16671,17 @@ async function exportGraphHtml(rootDir, outputPath) {
15807
16671
  } : null;
15808
16672
  })
15809
16673
  );
15810
- const rawHtml = await fs20.readFile(indexPath, "utf8");
16674
+ const rawHtml = await fs22.readFile(indexPath, "utf8");
15811
16675
  const scriptMatch = rawHtml.match(/<script type="module" crossorigin src="([^"]+)"><\/script>/);
15812
16676
  const styleMatch = rawHtml.match(/<link rel="stylesheet" crossorigin href="([^"]+)">/);
15813
- const scriptPath = scriptMatch?.[1] ? path25.join(paths.viewerDistDir, scriptMatch[1].replace(/^\//, "")) : null;
15814
- const stylePath = styleMatch?.[1] ? path25.join(paths.viewerDistDir, styleMatch[1].replace(/^\//, "")) : null;
16677
+ const scriptPath = scriptMatch?.[1] ? path27.join(paths.viewerDistDir, scriptMatch[1].replace(/^\//, "")) : null;
16678
+ const stylePath = styleMatch?.[1] ? path27.join(paths.viewerDistDir, styleMatch[1].replace(/^\//, "")) : null;
15815
16679
  if (!scriptPath || !await fileExists(scriptPath)) {
15816
16680
  throw new Error("Viewer script bundle not found. Run `pnpm build` first.");
15817
16681
  }
15818
- const script = await fs20.readFile(scriptPath, "utf8");
15819
- const style = stylePath && await fileExists(stylePath) ? await fs20.readFile(stylePath, "utf8") : "";
15820
- const report = await readJsonFile(path25.join(paths.wikiDir, "graph", "report.json"));
16682
+ const script = await fs22.readFile(scriptPath, "utf8");
16683
+ const style = stylePath && await fileExists(stylePath) ? await fs22.readFile(stylePath, "utf8") : "";
16684
+ const report = await readJsonFile(path27.join(paths.wikiDir, "graph", "report.json"));
15821
16685
  const embeddedData = JSON.stringify({ graph, pages: pages.filter(Boolean), report }, null, 2).replace(/</g, "\\u003c");
15822
16686
  const html = [
15823
16687
  "<!doctype html>",
@@ -15836,13 +16700,14 @@ async function exportGraphHtml(rootDir, outputPath) {
15836
16700
  "</html>",
15837
16701
  ""
15838
16702
  ].filter(Boolean).join("\n");
15839
- await fs20.mkdir(path25.dirname(outputPath), { recursive: true });
15840
- await fs20.writeFile(outputPath, html, "utf8");
15841
- return path25.resolve(outputPath);
16703
+ await fs22.mkdir(path27.dirname(outputPath), { recursive: true });
16704
+ await fs22.writeFile(outputPath, html, "utf8");
16705
+ return path27.resolve(outputPath);
15842
16706
  }
15843
16707
  export {
15844
16708
  acceptApproval,
15845
16709
  addInput,
16710
+ addManagedSource,
15846
16711
  archiveCandidate,
15847
16712
  assertProviderCapability,
15848
16713
  benchmarkVault,
@@ -15853,6 +16718,7 @@ export {
15853
16718
  createWebSearchAdapter,
15854
16719
  defaultVaultConfig,
15855
16720
  defaultVaultSchema,
16721
+ deleteManagedSource,
15856
16722
  explainGraphVault,
15857
16723
  exploreVault,
15858
16724
  exportGraphFormat,
@@ -15875,6 +16741,7 @@ export {
15875
16741
  listCandidates,
15876
16742
  listGodNodes,
15877
16743
  listGraphHyperedges,
16744
+ listManagedSourceRecords,
15878
16745
  listManifests,
15879
16746
  listPages,
15880
16747
  listSchedules,
@@ -15892,6 +16759,7 @@ export {
15892
16759
  readGraphReport,
15893
16760
  readPage,
15894
16761
  rejectApproval,
16762
+ reloadManagedSources,
15895
16763
  resolvePaths,
15896
16764
  runSchedule,
15897
16765
  runWatchCycle,