@swarmvaultai/engine 0.1.33 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -21,7 +21,7 @@ import {
21
21
  uniqueBy,
22
22
  writeFileIfChanged,
23
23
  writeJsonFile
24
- } from "./chunk-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";
@@ -1764,6 +1764,8 @@ var grammarAssetByLanguage = {
1764
1764
  java: { packageName: TREE_SITTER_RUNTIME_PACKAGE, relativePath: "wasm/tree-sitter-java.wasm" },
1765
1765
  kotlin: { packageName: TREE_SITTER_EXTRA_GRAMMARS_PACKAGE, relativePath: "out/tree-sitter-kotlin.wasm" },
1766
1766
  scala: { packageName: TREE_SITTER_EXTRA_GRAMMARS_PACKAGE, relativePath: "out/tree-sitter-scala.wasm" },
1767
+ lua: { packageName: TREE_SITTER_EXTRA_GRAMMARS_PACKAGE, relativePath: "out/tree-sitter-lua.wasm" },
1768
+ zig: { packageName: TREE_SITTER_EXTRA_GRAMMARS_PACKAGE, relativePath: "out/tree-sitter-zig.wasm" },
1767
1769
  csharp: { packageName: TREE_SITTER_RUNTIME_PACKAGE, relativePath: "wasm/tree-sitter-c-sharp.wasm" },
1768
1770
  c: { packageName: TREE_SITTER_RUNTIME_PACKAGE, relativePath: "wasm/tree-sitter-cpp.wasm" },
1769
1771
  cpp: { packageName: TREE_SITTER_RUNTIME_PACKAGE, relativePath: "wasm/tree-sitter-cpp.wasm" },
@@ -1828,7 +1830,7 @@ function normalizeSymbolReference(value) {
1828
1830
  return lastSegment.replace(/[,:;]+$/g, "").trim();
1829
1831
  }
1830
1832
  function stripCodeExtension(filePath) {
1831
- return filePath.replace(/\.(?:[cm]?jsx?|tsx?|mts|cts|py|go|rs|java|kt|kts|scala|sc|cs|php|c|cc|cpp|cxx|h|hh|hpp|hxx)$/i, "");
1833
+ return filePath.replace(/\.(?:[cm]?jsx?|tsx?|mts|cts|py|go|rs|java|kt|kts|scala|sc|lua|zig|cs|php|c|cc|cpp|cxx|h|hh|hpp|hxx)$/i, "");
1832
1834
  }
1833
1835
  function manifestModuleName(manifest, language) {
1834
1836
  const repoPath = manifest.repoRelativePath ?? path5.basename(manifest.originalPath ?? manifest.storedPath);
@@ -2112,7 +2114,7 @@ function descendantTypeNames(node) {
2112
2114
  );
2113
2115
  }
2114
2116
  function quotedPath(value) {
2115
- return value.replace(/^["'<]+|[">]+$/g, "").trim();
2117
+ return value.replace(/^['"<]+|['">]+$/g, "").trim();
2116
2118
  }
2117
2119
  function diagnosticsFromTree(rootNode) {
2118
2120
  if (!rootNode.hasError) {
@@ -2262,6 +2264,43 @@ function parseScalaImport(text) {
2262
2264
  reExport: false
2263
2265
  }));
2264
2266
  }
2267
+ function parseLuaRequire(node) {
2268
+ const stringNode = node.descendantsOfType("string").find((item) => item !== null);
2269
+ const identifiers = node.descendantsOfType("identifier").filter((item) => item !== null).map((item) => item.text.trim());
2270
+ if (!stringNode || !identifiers.includes("require")) {
2271
+ return void 0;
2272
+ }
2273
+ const specifier = quotedPath(stringNode.text);
2274
+ if (!specifier) {
2275
+ return void 0;
2276
+ }
2277
+ return {
2278
+ specifier,
2279
+ importedSymbols: [],
2280
+ isExternal: !/^[A-Za-z_][A-Za-z0-9_]*(?:[./][A-Za-z_][A-Za-z0-9_]*)*$/.test(specifier),
2281
+ reExport: false
2282
+ };
2283
+ }
2284
+ function parseZigImport(node) {
2285
+ if (node.type !== "variable_declaration") {
2286
+ return void 0;
2287
+ }
2288
+ const importCall = findNamedChild(node, "builtin_function");
2289
+ if (!importCall || nodeText(findNamedChild(importCall, "builtin_identifier") ?? importCall.namedChildren.at(0) ?? null) !== "@import") {
2290
+ return void 0;
2291
+ }
2292
+ const stringNode = importCall.descendantsOfType("string_content").find((item) => item !== null);
2293
+ const specifier = stringNode?.text.trim();
2294
+ if (!specifier) {
2295
+ return void 0;
2296
+ }
2297
+ return {
2298
+ specifier,
2299
+ importedSymbols: [],
2300
+ isExternal: !specifier.endsWith(".zig") && !specifier.includes("/") && !specifier.startsWith("."),
2301
+ reExport: false
2302
+ };
2303
+ }
2265
2304
  function parseCSharpUsing(text) {
2266
2305
  const aliasMatch = text.trim().match(/^using\s+([A-Za-z_]\w*)\s*=\s*([^;]+);$/);
2267
2306
  if (aliasMatch) {
@@ -2386,6 +2425,28 @@ function scalaDefinitionKind(node) {
2386
2425
  }
2387
2426
  return void 0;
2388
2427
  }
2428
+ function luaFunctionName(node) {
2429
+ if (!node) {
2430
+ return void 0;
2431
+ }
2432
+ if (node.type === "identifier") {
2433
+ return node.text.trim();
2434
+ }
2435
+ if (node.type === "variable") {
2436
+ const identifiers = node.descendantsOfType("identifier").filter((item) => item !== null).map((item) => item.text.trim()).filter(Boolean);
2437
+ return identifiers.length > 0 ? identifiers.join(".") : void 0;
2438
+ }
2439
+ return extractIdentifier(node);
2440
+ }
2441
+ function zigDeclarationKind(node) {
2442
+ if (findNamedChild(node, "struct_declaration")) {
2443
+ return "struct";
2444
+ }
2445
+ if (findNamedChild(node, "enum_declaration")) {
2446
+ return "enum";
2447
+ }
2448
+ return void 0;
2449
+ }
2389
2450
  function pythonCodeAnalysis(manifest, rootNode, diagnostics) {
2390
2451
  const imports = [];
2391
2452
  const draftSymbols = [];
@@ -2814,6 +2875,136 @@ function scalaCodeAnalysis(manifest, rootNode, diagnostics) {
2814
2875
  namespace: packageName
2815
2876
  });
2816
2877
  }
2878
+ function luaCodeAnalysis(manifest, rootNode, diagnostics) {
2879
+ const imports = [];
2880
+ const draftSymbols = [];
2881
+ const exportLabels = [];
2882
+ for (const child of rootNode.namedChildren) {
2883
+ if (!child) {
2884
+ continue;
2885
+ }
2886
+ if (child.type === "local_variable_declaration" || child.type === "assignment_statement") {
2887
+ const parsed = parseLuaRequire(child);
2888
+ if (parsed) {
2889
+ imports.push(parsed);
2890
+ }
2891
+ continue;
2892
+ }
2893
+ if (!["function_definition_statement", "local_function_definition_statement"].includes(child.type)) {
2894
+ continue;
2895
+ }
2896
+ const name = luaFunctionName(child.childForFieldName("name") ?? child.namedChildren.at(0) ?? null);
2897
+ if (!name) {
2898
+ continue;
2899
+ }
2900
+ draftSymbols.push({
2901
+ name,
2902
+ kind: "function",
2903
+ signature: singleLineSignature(child.text),
2904
+ exported: child.type !== "local_function_definition_statement",
2905
+ callNames: [],
2906
+ extendsNames: [],
2907
+ implementsNames: [],
2908
+ bodyText: nodeText(findNamedChild(child, "block") ?? child.childForFieldName("body")) || child.text
2909
+ });
2910
+ if (child.type !== "local_function_definition_statement") {
2911
+ exportLabels.push(name);
2912
+ }
2913
+ }
2914
+ return finalizeCodeAnalysis(manifest, "lua", imports, draftSymbols, exportLabels, diagnostics);
2915
+ }
2916
+ function zigCodeAnalysis(manifest, rootNode, diagnostics) {
2917
+ const imports = [];
2918
+ const draftSymbols = [];
2919
+ const exportLabels = [];
2920
+ const pushStructMembers = (structNode, scopeName) => {
2921
+ if (!structNode) {
2922
+ return;
2923
+ }
2924
+ for (const child of structNode.namedChildren) {
2925
+ if (!child || child.type !== "function_declaration") {
2926
+ continue;
2927
+ }
2928
+ const functionName = extractIdentifier(child.childForFieldName("name") ?? findNamedChild(child, "identifier"));
2929
+ if (!functionName) {
2930
+ continue;
2931
+ }
2932
+ const exported = /\bpub\b/.test(child.text);
2933
+ const symbolName = `${scopeName}.${functionName}`;
2934
+ draftSymbols.push({
2935
+ name: symbolName,
2936
+ kind: "function",
2937
+ signature: singleLineSignature(child.text),
2938
+ exported,
2939
+ callNames: [],
2940
+ extendsNames: [],
2941
+ implementsNames: [],
2942
+ bodyText: nodeText(findNamedChild(child, "block") ?? child.childForFieldName("body")) || child.text
2943
+ });
2944
+ if (exported) {
2945
+ exportLabels.push(symbolName);
2946
+ }
2947
+ }
2948
+ };
2949
+ for (const child of rootNode.namedChildren) {
2950
+ if (!child) {
2951
+ continue;
2952
+ }
2953
+ if (child.type === "variable_declaration") {
2954
+ const parsedImport = parseZigImport(child);
2955
+ if (parsedImport) {
2956
+ imports.push(parsedImport);
2957
+ continue;
2958
+ }
2959
+ const name = extractIdentifier(child.childForFieldName("name") ?? findNamedChild(child, "identifier"));
2960
+ const kind = zigDeclarationKind(child);
2961
+ if (!name || !kind) {
2962
+ continue;
2963
+ }
2964
+ const declarationNode = findNamedChild(child, "struct_declaration") ?? findNamedChild(child, "enum_declaration");
2965
+ const exported2 = /\bpub\b/.test(child.text);
2966
+ draftSymbols.push({
2967
+ name,
2968
+ kind,
2969
+ signature: singleLineSignature(child.text),
2970
+ exported: exported2,
2971
+ callNames: [],
2972
+ extendsNames: [],
2973
+ implementsNames: [],
2974
+ bodyText: nodeText(declarationNode) || child.text
2975
+ });
2976
+ if (exported2) {
2977
+ exportLabels.push(name);
2978
+ }
2979
+ if (kind === "struct") {
2980
+ pushStructMembers(declarationNode, name);
2981
+ }
2982
+ continue;
2983
+ }
2984
+ if (child.type !== "function_declaration") {
2985
+ continue;
2986
+ }
2987
+ const functionName = extractIdentifier(child.childForFieldName("name") ?? findNamedChild(child, "identifier"));
2988
+ if (!functionName) {
2989
+ continue;
2990
+ }
2991
+ const exported = /\bpub\b/.test(child.text);
2992
+ draftSymbols.push({
2993
+ name: functionName,
2994
+ kind: "function",
2995
+ signature: singleLineSignature(child.text),
2996
+ exported,
2997
+ callNames: [],
2998
+ extendsNames: [],
2999
+ implementsNames: [],
3000
+ bodyText: nodeText(findNamedChild(child, "block") ?? child.childForFieldName("body")) || child.text
3001
+ });
3002
+ if (exported) {
3003
+ exportLabels.push(functionName);
3004
+ }
3005
+ }
3006
+ return finalizeCodeAnalysis(manifest, "zig", imports, draftSymbols, exportLabels, diagnostics);
3007
+ }
2817
3008
  function csharpCodeAnalysis(manifest, rootNode, diagnostics) {
2818
3009
  const imports = [];
2819
3010
  const draftSymbols = [];
@@ -3246,6 +3437,10 @@ async function analyzeTreeSitterCode(manifest, content, language) {
3246
3437
  return { code: kotlinCodeAnalysis(manifest, tree.rootNode, diagnostics), rationales };
3247
3438
  case "scala":
3248
3439
  return { code: scalaCodeAnalysis(manifest, tree.rootNode, diagnostics), rationales };
3440
+ case "lua":
3441
+ return { code: luaCodeAnalysis(manifest, tree.rootNode, diagnostics), rationales };
3442
+ case "zig":
3443
+ return { code: zigCodeAnalysis(manifest, tree.rootNode, diagnostics), rationales };
3249
3444
  case "csharp":
3250
3445
  return { code: csharpCodeAnalysis(manifest, tree.rootNode, diagnostics), rationales };
3251
3446
  case "php":
@@ -3532,7 +3727,7 @@ function makeRationale2(manifest, index, text, kind, symbolName) {
3532
3727
  };
3533
3728
  }
3534
3729
  function stripCodeExtension2(filePath) {
3535
- return filePath.replace(/\.(?:[cm]?jsx?|tsx?|mts|cts|py|go|rs|java|kt|kts|scala|sc|cs|php|c|cc|cpp|cxx|h|hh|hpp|hxx)$/i, "");
3730
+ return filePath.replace(/\.(?:[cm]?jsx?|tsx?|mts|cts|py|go|rs|java|kt|kts|scala|sc|lua|zig|cs|php|c|cc|cpp|cxx|h|hh|hpp|hxx)$/i, "");
3536
3731
  }
3537
3732
  function manifestModuleName2(manifest, language) {
3538
3733
  const repoPath = manifest.repoRelativePath ?? path6.basename(manifest.originalPath ?? manifest.storedPath);
@@ -3869,6 +4064,12 @@ function inferCodeLanguage(filePath, mimeType = "") {
3869
4064
  if (extension === ".scala" || extension === ".sc") {
3870
4065
  return "scala";
3871
4066
  }
4067
+ if (extension === ".lua") {
4068
+ return "lua";
4069
+ }
4070
+ if (extension === ".zig") {
4071
+ return "zig";
4072
+ }
3872
4073
  if (extension === ".cs") {
3873
4074
  return "csharp";
3874
4075
  }
@@ -3977,6 +4178,10 @@ function candidateExtensionsFor(language) {
3977
4178
  return [".kt", ".kts"];
3978
4179
  case "scala":
3979
4180
  return [".scala", ".sc"];
4181
+ case "lua":
4182
+ return [".lua"];
4183
+ case "zig":
4184
+ return [".zig"];
3980
4185
  case "csharp":
3981
4186
  return [".cs"];
3982
4187
  case "php":
@@ -4049,6 +4254,20 @@ async function buildCodeIndex(rootDir, manifests, analyses) {
4049
4254
  }
4050
4255
  }
4051
4256
  break;
4257
+ case "lua":
4258
+ recordAlias(aliases, basename);
4259
+ if (repoRelativePath) {
4260
+ const repoWithoutExt = stripCodeExtension2(repoRelativePath);
4261
+ recordAlias(aliases, repoWithoutExt.replace(/\//g, "."));
4262
+ if (repoWithoutExt.endsWith("/init")) {
4263
+ recordAlias(aliases, repoWithoutExt.slice(0, -"/init".length));
4264
+ recordAlias(aliases, repoWithoutExt.slice(0, -"/init".length).replace(/\//g, "."));
4265
+ }
4266
+ }
4267
+ break;
4268
+ case "zig":
4269
+ recordAlias(aliases, basename);
4270
+ break;
4052
4271
  case "php":
4053
4272
  if (normalizedNamespace) {
4054
4273
  recordAlias(aliases, `${normalizedNamespace}\\${basename}`);
@@ -4135,6 +4354,16 @@ function resolveRustAliases(manifest, specifier) {
4135
4354
  `crate${currentParts.length > 1 ? `::${currentParts.slice(0, -1).join("::")}` : ""}::${specifier.slice("super::".length)}`.replace(/::+/g, "::").replace(/::$/, "")
4136
4355
  ];
4137
4356
  }
4357
+ function luaSpecifierLooksLocal(specifier) {
4358
+ return /^[A-Za-z_][A-Za-z0-9_]*(?:[./][A-Za-z_][A-Za-z0-9_]*)*$/.test(specifier);
4359
+ }
4360
+ function resolveLuaModuleCandidates(specifier) {
4361
+ const normalized = normalizeAlias(specifier.replace(/\./g, "/"));
4362
+ if (!normalized) {
4363
+ return [];
4364
+ }
4365
+ return uniqueBy([`${normalized}.lua`, path6.posix.join(normalized, "init.lua")], (item) => item);
4366
+ }
4138
4367
  function findImportCandidates(manifest, codeImport, lookup) {
4139
4368
  const language = manifest.language ?? inferCodeLanguage(manifest.originalPath ?? manifest.storedPath, manifest.mimeType);
4140
4369
  const repoRelativePath = manifest.repoRelativePath ? normalizeAlias(manifest.repoRelativePath) : void 0;
@@ -4158,6 +4387,10 @@ function findImportCandidates(manifest, codeImport, lookup) {
4158
4387
  case "scala":
4159
4388
  case "csharp":
4160
4389
  return aliasMatches(lookup, codeImport.specifier);
4390
+ case "lua":
4391
+ return luaSpecifierLooksLocal(codeImport.specifier) ? repoPathMatches(lookup, ...resolveLuaModuleCandidates(codeImport.specifier)) : aliasMatches(lookup, codeImport.specifier, codeImport.specifier.replace(/\./g, "/"));
4392
+ case "zig":
4393
+ return repoRelativePath && (!codeImport.isExternal || codeImport.specifier.endsWith(".zig")) ? repoPathMatches(lookup, ...importResolutionCandidates(repoRelativePath, codeImport.specifier, candidateExtensionsFor(language))) : aliasMatches(lookup, codeImport.specifier);
4161
4394
  case "php":
4162
4395
  case "ruby":
4163
4396
  case "powershell":
@@ -4205,6 +4438,10 @@ function importLooksLocal(manifest, codeImport, candidates) {
4205
4438
  case "kotlin":
4206
4439
  case "scala":
4207
4440
  return !codeImport.isExternal;
4441
+ case "lua":
4442
+ return luaSpecifierLooksLocal(codeImport.specifier);
4443
+ case "zig":
4444
+ return !codeImport.isExternal || codeImport.specifier.endsWith(".zig");
4208
4445
  default:
4209
4446
  return false;
4210
4447
  }
@@ -4724,9 +4961,123 @@ function aggregateManifestSourceClass(manifests, sourceIds) {
4724
4961
  return aggregateSourceClass(sourceIds.map((sourceId) => byId.get(sourceId)));
4725
4962
  }
4726
4963
 
4727
- // src/watch-state.ts
4964
+ // src/source-registry.ts
4728
4965
  import fs9 from "fs/promises";
4729
4966
  import path10 from "path";
4967
+ var MANAGED_SOURCES_VERSION = 1;
4968
+ function repoRootFromManifest(manifest) {
4969
+ if (manifest.originType !== "file" || !manifest.originalPath || !manifest.repoRelativePath) {
4970
+ return null;
4971
+ }
4972
+ const repoDir = path10.posix.dirname(manifest.repoRelativePath);
4973
+ const fileDir = path10.dirname(path10.resolve(manifest.originalPath));
4974
+ if (repoDir === "." || !repoDir) {
4975
+ return fileDir;
4976
+ }
4977
+ const segments = repoDir.split("/").filter(Boolean);
4978
+ return path10.resolve(fileDir, ...segments.map(() => ".."));
4979
+ }
4980
+ async function loadManifestArtifacts(paths) {
4981
+ const entries = await fs9.readdir(paths.manifestsDir, { withFileTypes: true }).catch(() => []);
4982
+ const manifests = await Promise.all(
4983
+ entries.filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map(async (entry) => await readJsonFile(path10.join(paths.manifestsDir, entry.name)))
4984
+ );
4985
+ return manifests.filter((manifest) => Boolean(manifest?.sourceId));
4986
+ }
4987
+ function buildLegacyDirectoryEntry(repoRoot, manifests) {
4988
+ const repoTitle = path10.basename(repoRoot) || repoRoot;
4989
+ const sourceIds = manifests.map((manifest) => manifest.sourceId).sort((left, right) => left.localeCompare(right));
4990
+ const createdAt = manifests.map((manifest) => manifest.createdAt).sort((left, right) => left.localeCompare(right))[0] ?? (/* @__PURE__ */ new Date()).toISOString();
4991
+ const updatedAt = manifests.map((manifest) => manifest.updatedAt).sort((left, right) => right.localeCompare(left))[0] ?? createdAt;
4992
+ return {
4993
+ id: `directory-${slugify(repoTitle)}-${sha256(repoRoot).slice(0, 8)}`,
4994
+ kind: "directory",
4995
+ title: repoTitle,
4996
+ path: repoRoot,
4997
+ repoRoot,
4998
+ createdAt,
4999
+ updatedAt,
5000
+ status: "ready",
5001
+ sourceIds,
5002
+ lastSyncAt: updatedAt,
5003
+ lastSyncStatus: "success",
5004
+ lastSyncCounts: {
5005
+ scannedCount: manifests.length,
5006
+ importedCount: manifests.length,
5007
+ updatedCount: 0,
5008
+ removedCount: 0,
5009
+ skippedCount: 0
5010
+ }
5011
+ };
5012
+ }
5013
+ async function buildLegacyArtifact(paths) {
5014
+ const manifests = await loadManifestArtifacts(paths);
5015
+ const manifestsByRepoRoot = /* @__PURE__ */ new Map();
5016
+ for (const manifest of manifests) {
5017
+ const repoRoot = repoRootFromManifest(manifest);
5018
+ if (!repoRoot) {
5019
+ continue;
5020
+ }
5021
+ const key = path10.resolve(repoRoot);
5022
+ const bucket = manifestsByRepoRoot.get(key) ?? [];
5023
+ bucket.push(manifest);
5024
+ manifestsByRepoRoot.set(key, bucket);
5025
+ }
5026
+ const repoRoots = [...manifestsByRepoRoot.entries()].filter(([, repoManifests]) => {
5027
+ return repoManifests.length > 1 || repoManifests.some((manifest) => manifest.sourceKind === "code");
5028
+ });
5029
+ return {
5030
+ version: MANAGED_SOURCES_VERSION,
5031
+ sources: repoRoots.sort((left, right) => left[0].localeCompare(right[0])).map(([repoRoot, repoManifests]) => buildLegacyDirectoryEntry(repoRoot, repoManifests))
5032
+ };
5033
+ }
5034
+ async function ensureManagedSourcesArtifact(rootDir) {
5035
+ const { paths } = await initWorkspace(rootDir);
5036
+ if (await fileExists(paths.managedSourcesPath)) {
5037
+ const existing = await readJsonFile(paths.managedSourcesPath);
5038
+ if (existing?.version === MANAGED_SOURCES_VERSION && Array.isArray(existing.sources)) {
5039
+ return {
5040
+ version: MANAGED_SOURCES_VERSION,
5041
+ sources: [...existing.sources].sort(
5042
+ (left, right) => left.createdAt.localeCompare(right.createdAt) || left.id.localeCompare(right.id)
5043
+ )
5044
+ };
5045
+ }
5046
+ }
5047
+ const artifact = await buildLegacyArtifact(paths);
5048
+ await writeJsonFile(paths.managedSourcesPath, artifact);
5049
+ return artifact;
5050
+ }
5051
+ async function loadManagedSources(rootDir) {
5052
+ const artifact = await ensureManagedSourcesArtifact(rootDir);
5053
+ return artifact.sources;
5054
+ }
5055
+ async function readManagedSourcesIfPresent(rootDir) {
5056
+ const { paths } = await initWorkspace(rootDir);
5057
+ if (!await fileExists(paths.managedSourcesPath)) {
5058
+ return null;
5059
+ }
5060
+ const existing = await readJsonFile(paths.managedSourcesPath);
5061
+ if (!existing?.version || !Array.isArray(existing.sources)) {
5062
+ return null;
5063
+ }
5064
+ return existing.sources;
5065
+ }
5066
+ async function saveManagedSources(rootDir, sources) {
5067
+ const { paths } = await initWorkspace(rootDir);
5068
+ await writeJsonFile(paths.managedSourcesPath, {
5069
+ version: MANAGED_SOURCES_VERSION,
5070
+ sources: [...sources].sort((left, right) => left.createdAt.localeCompare(right.createdAt) || left.id.localeCompare(right.id))
5071
+ });
5072
+ }
5073
+ async function managedSourceWorkingDir(rootDir, sourceId) {
5074
+ const { paths } = await initWorkspace(rootDir);
5075
+ return path10.join(paths.managedSourcesDir, sourceId);
5076
+ }
5077
+
5078
+ // src/watch-state.ts
5079
+ import fs10 from "fs/promises";
5080
+ import path11 from "path";
4730
5081
  import matter2 from "gray-matter";
4731
5082
  function pendingEntryKey(entry) {
4732
5083
  return entry.path;
@@ -4740,7 +5091,7 @@ function normalizeRelativePath(rootDir, filePath) {
4740
5091
  if (!filePath) {
4741
5092
  return void 0;
4742
5093
  }
4743
- return toPosix(path10.relative(rootDir, path10.resolve(filePath)));
5094
+ return toPosix(path11.relative(rootDir, path11.resolve(filePath)));
4744
5095
  }
4745
5096
  async function readPendingSemanticRefresh(rootDir) {
4746
5097
  const { paths } = await initWorkspace(rootDir);
@@ -4834,11 +5185,11 @@ async function markPagesStaleForSources(rootDir, sourceIds) {
4834
5185
  if (page.freshness !== "stale" || !page.sourceIds.some((sourceId) => affectedSourceIds.has(sourceId))) {
4835
5186
  continue;
4836
5187
  }
4837
- const absolutePath = path10.join(paths.wikiDir, page.path);
5188
+ const absolutePath = path11.join(paths.wikiDir, page.path);
4838
5189
  if (!await fileExists(absolutePath)) {
4839
5190
  continue;
4840
5191
  }
4841
- const raw = await fs9.readFile(absolutePath, "utf8");
5192
+ const raw = await fs10.readFile(absolutePath, "utf8");
4842
5193
  const parsed = matter2(raw);
4843
5194
  if (parsed.data.freshness === "stale") {
4844
5195
  continue;
@@ -4888,7 +5239,7 @@ function inferKind(mimeType, filePath) {
4888
5239
  return "binary";
4889
5240
  }
4890
5241
  function isRstFilePath(filePath) {
4891
- const extension = path11.extname(filePath).toLowerCase();
5242
+ const extension = path12.extname(filePath).toLowerCase();
4892
5243
  return extension === ".rst" || extension === ".rest";
4893
5244
  }
4894
5245
  function titleFromText(fallback, content, filePath) {
@@ -5031,7 +5382,7 @@ function normalizeIngestOptions(options) {
5031
5382
  return {
5032
5383
  includeAssets: options?.includeAssets ?? true,
5033
5384
  maxAssetSize: Math.max(0, Math.floor(options?.maxAssetSize ?? DEFAULT_MAX_ASSET_SIZE)),
5034
- repoRoot: options?.repoRoot ? path11.resolve(options.repoRoot) : void 0,
5385
+ repoRoot: options?.repoRoot ? path12.resolve(options.repoRoot) : void 0,
5035
5386
  include: (options?.include ?? []).map((pattern) => pattern.trim()).filter(Boolean),
5036
5387
  exclude: (options?.exclude ?? []).map((pattern) => pattern.trim()).filter(Boolean),
5037
5388
  maxFiles: Math.max(1, Math.floor(options?.maxFiles ?? DEFAULT_MAX_DIRECTORY_FILES)),
@@ -5051,27 +5402,27 @@ async function resolveRepoIngestOptions(rootDir, options) {
5051
5402
  }
5052
5403
  function matchesAnyGlob2(relativePath, patterns) {
5053
5404
  return patterns.some(
5054
- (pattern) => path11.matchesGlob(relativePath, pattern) || path11.matchesGlob(path11.posix.basename(relativePath), pattern)
5405
+ (pattern) => path12.matchesGlob(relativePath, pattern) || path12.matchesGlob(path12.posix.basename(relativePath), pattern)
5055
5406
  );
5056
5407
  }
5057
5408
  function supportedDirectoryKind(sourceKind) {
5058
5409
  return sourceKind !== "binary";
5059
5410
  }
5060
5411
  async function findNearestGitRoot2(startPath) {
5061
- let current = path11.resolve(startPath);
5412
+ let current = path12.resolve(startPath);
5062
5413
  try {
5063
- const stat = await fs10.stat(current);
5414
+ const stat = await fs11.stat(current);
5064
5415
  if (!stat.isDirectory()) {
5065
- current = path11.dirname(current);
5416
+ current = path12.dirname(current);
5066
5417
  }
5067
5418
  } catch {
5068
- current = path11.dirname(current);
5419
+ current = path12.dirname(current);
5069
5420
  }
5070
5421
  while (true) {
5071
- if (await fileExists(path11.join(current, ".git"))) {
5422
+ if (await fileExists(path12.join(current, ".git"))) {
5072
5423
  return current;
5073
5424
  }
5074
- const parent = path11.dirname(current);
5425
+ const parent = path12.dirname(current);
5075
5426
  if (parent === current) {
5076
5427
  return null;
5077
5428
  }
@@ -5079,26 +5430,26 @@ async function findNearestGitRoot2(startPath) {
5079
5430
  }
5080
5431
  }
5081
5432
  function withinRoot(rootPath, targetPath) {
5082
- const relative = path11.relative(rootPath, targetPath);
5083
- return relative === "" || !relative.startsWith("..") && !path11.isAbsolute(relative);
5433
+ const relative = path12.relative(rootPath, targetPath);
5434
+ return relative === "" || !relative.startsWith("..") && !path12.isAbsolute(relative);
5084
5435
  }
5085
- function repoRootFromManifest(manifest) {
5436
+ function repoRootFromManifest2(manifest) {
5086
5437
  if (manifest.originType !== "file" || !manifest.originalPath || !manifest.repoRelativePath) {
5087
5438
  return null;
5088
5439
  }
5089
- const repoDir = path11.posix.dirname(manifest.repoRelativePath);
5090
- const fileDir = path11.dirname(path11.resolve(manifest.originalPath));
5440
+ const repoDir = path12.posix.dirname(manifest.repoRelativePath);
5441
+ const fileDir = path12.dirname(path12.resolve(manifest.originalPath));
5091
5442
  if (repoDir === "." || !repoDir) {
5092
5443
  return fileDir;
5093
5444
  }
5094
5445
  const segments = repoDir.split("/").filter(Boolean);
5095
- return path11.resolve(fileDir, ...segments.map(() => ".."));
5446
+ return path12.resolve(fileDir, ...segments.map(() => ".."));
5096
5447
  }
5097
5448
  function repoRelativePathFor(absolutePath, repoRoot) {
5098
5449
  if (!repoRoot || !withinRoot(repoRoot, absolutePath)) {
5099
5450
  return void 0;
5100
5451
  }
5101
- const relative = toPosix(path11.relative(repoRoot, absolutePath));
5452
+ const relative = toPosix(path12.relative(repoRoot, absolutePath));
5102
5453
  return relative && !relative.startsWith("..") ? relative : void 0;
5103
5454
  }
5104
5455
  function normalizeOriginUrl(input) {
@@ -5430,7 +5781,7 @@ function buildCompositeHash(payloadBytes, attachments = []) {
5430
5781
  return sha256(`${sha256(payloadBytes)}|${attachmentSignature}`);
5431
5782
  }
5432
5783
  function sanitizeAssetRelativePath(value) {
5433
- const normalized = path11.posix.normalize(value.replace(/\\/g, "/"));
5784
+ const normalized = path12.posix.normalize(value.replace(/\\/g, "/"));
5434
5785
  const segments = normalized.split("/").filter(Boolean).map((segment) => {
5435
5786
  if (segment === ".") {
5436
5787
  return "";
@@ -5450,7 +5801,7 @@ function normalizeLocalReference(value) {
5450
5801
  return null;
5451
5802
  }
5452
5803
  const lowered = candidate.toLowerCase();
5453
- if (lowered.startsWith("http://") || lowered.startsWith("https://") || lowered.startsWith("data:") || lowered.startsWith("mailto:") || lowered.startsWith("#") || path11.isAbsolute(candidate)) {
5804
+ if (lowered.startsWith("http://") || lowered.startsWith("https://") || lowered.startsWith("data:") || lowered.startsWith("mailto:") || lowered.startsWith("#") || path12.isAbsolute(candidate)) {
5454
5805
  return null;
5455
5806
  }
5456
5807
  return candidate.replace(/\\/g, "/");
@@ -5528,12 +5879,12 @@ async function convertHtmlToMarkdown(html, url) {
5528
5879
  };
5529
5880
  }
5530
5881
  async function readManifestByHash(manifestsDir, contentHash) {
5531
- const entries = await fs10.readdir(manifestsDir, { withFileTypes: true }).catch(() => []);
5882
+ const entries = await fs11.readdir(manifestsDir, { withFileTypes: true }).catch(() => []);
5532
5883
  for (const entry of entries) {
5533
5884
  if (!entry.isFile() || !entry.name.endsWith(".json")) {
5534
5885
  continue;
5535
5886
  }
5536
- const manifest = await readJsonFile(path11.join(manifestsDir, entry.name));
5887
+ const manifest = await readJsonFile(path12.join(manifestsDir, entry.name));
5537
5888
  if (manifest?.contentHash === contentHash) {
5538
5889
  return manifest;
5539
5890
  }
@@ -5541,12 +5892,12 @@ async function readManifestByHash(manifestsDir, contentHash) {
5541
5892
  return null;
5542
5893
  }
5543
5894
  async function readManifestByOrigin(manifestsDir, prepared) {
5544
- const entries = await fs10.readdir(manifestsDir, { withFileTypes: true }).catch(() => []);
5895
+ const entries = await fs11.readdir(manifestsDir, { withFileTypes: true }).catch(() => []);
5545
5896
  for (const entry of entries) {
5546
5897
  if (!entry.isFile() || !entry.name.endsWith(".json")) {
5547
5898
  continue;
5548
5899
  }
5549
- const manifest = await readJsonFile(path11.join(manifestsDir, entry.name));
5900
+ const manifest = await readJsonFile(path12.join(manifestsDir, entry.name));
5550
5901
  if (manifest && manifestMatchesOrigin(manifest, prepared)) {
5551
5902
  return manifest;
5552
5903
  }
@@ -5557,12 +5908,12 @@ async function loadGitignoreMatcher(repoRoot, enabled) {
5557
5908
  if (!enabled) {
5558
5909
  return null;
5559
5910
  }
5560
- const gitignorePath = path11.join(repoRoot, ".gitignore");
5911
+ const gitignorePath = path12.join(repoRoot, ".gitignore");
5561
5912
  if (!await fileExists(gitignorePath)) {
5562
5913
  return null;
5563
5914
  }
5564
5915
  const matcher = ignore();
5565
- matcher.add(await fs10.readFile(gitignorePath, "utf8"));
5916
+ matcher.add(await fs11.readFile(gitignorePath, "utf8"));
5566
5917
  return matcher;
5567
5918
  }
5568
5919
  function builtInIgnoreReason(relativePath) {
@@ -5586,23 +5937,23 @@ async function collectDirectoryFiles(rootDir, inputDir, repoRoot, options) {
5586
5937
  if (!currentDir) {
5587
5938
  continue;
5588
5939
  }
5589
- const entries = await fs10.readdir(currentDir, { withFileTypes: true });
5940
+ const entries = await fs11.readdir(currentDir, { withFileTypes: true });
5590
5941
  entries.sort((left, right) => left.name.localeCompare(right.name));
5591
5942
  for (const entry of entries) {
5592
- const absolutePath = path11.join(currentDir, entry.name);
5593
- const relativeToRepo = repoRelativePathFor(absolutePath, repoRoot) ?? toPosix(path11.relative(inputDir, absolutePath));
5943
+ const absolutePath = path12.join(currentDir, entry.name);
5944
+ const relativeToRepo = repoRelativePathFor(absolutePath, repoRoot) ?? toPosix(path12.relative(inputDir, absolutePath));
5594
5945
  const relativePath = relativeToRepo || entry.name;
5595
5946
  const builtInReason = builtInIgnoreReason(relativePath);
5596
5947
  if (builtInReason) {
5597
- skipped.push({ path: toPosix(path11.relative(rootDir, absolutePath)), reason: builtInReason });
5948
+ skipped.push({ path: toPosix(path12.relative(rootDir, absolutePath)), reason: builtInReason });
5598
5949
  continue;
5599
5950
  }
5600
5951
  if (matcher?.ignores(relativePath)) {
5601
- skipped.push({ path: toPosix(path11.relative(rootDir, absolutePath)), reason: "gitignore" });
5952
+ skipped.push({ path: toPosix(path12.relative(rootDir, absolutePath)), reason: "gitignore" });
5602
5953
  continue;
5603
5954
  }
5604
5955
  if (matchesAnyGlob2(relativePath, options.exclude)) {
5605
- skipped.push({ path: toPosix(path11.relative(rootDir, absolutePath)), reason: "exclude_glob" });
5956
+ skipped.push({ path: toPosix(path12.relative(rootDir, absolutePath)), reason: "exclude_glob" });
5606
5957
  continue;
5607
5958
  }
5608
5959
  if (entry.isDirectory()) {
@@ -5610,26 +5961,26 @@ async function collectDirectoryFiles(rootDir, inputDir, repoRoot, options) {
5610
5961
  continue;
5611
5962
  }
5612
5963
  if (!entry.isFile()) {
5613
- skipped.push({ path: toPosix(path11.relative(rootDir, absolutePath)), reason: "unsupported_entry" });
5964
+ skipped.push({ path: toPosix(path12.relative(rootDir, absolutePath)), reason: "unsupported_entry" });
5614
5965
  continue;
5615
5966
  }
5616
5967
  if (options.include.length > 0 && !matchesAnyGlob2(relativePath, options.include)) {
5617
- skipped.push({ path: toPosix(path11.relative(rootDir, absolutePath)), reason: "include_glob" });
5968
+ skipped.push({ path: toPosix(path12.relative(rootDir, absolutePath)), reason: "include_glob" });
5618
5969
  continue;
5619
5970
  }
5620
5971
  const mimeType = guessMimeType(absolutePath);
5621
5972
  const sourceKind = inferKind(mimeType, absolutePath);
5622
5973
  const sourceClass = sourceClassForRelativePath(relativePath, options);
5623
5974
  if (!supportedDirectoryKind(sourceKind)) {
5624
- skipped.push({ path: toPosix(path11.relative(rootDir, absolutePath)), reason: `unsupported_kind:${sourceKind}` });
5975
+ skipped.push({ path: toPosix(path12.relative(rootDir, absolutePath)), reason: `unsupported_kind:${sourceKind}` });
5625
5976
  continue;
5626
5977
  }
5627
5978
  if (!options.extractClasses.includes(sourceClass)) {
5628
- skipped.push({ path: toPosix(path11.relative(rootDir, absolutePath)), reason: `source_class:${sourceClass}` });
5979
+ skipped.push({ path: toPosix(path12.relative(rootDir, absolutePath)), reason: `source_class:${sourceClass}` });
5629
5980
  continue;
5630
5981
  }
5631
5982
  if (files.length >= options.maxFiles) {
5632
- skipped.push({ path: toPosix(path11.relative(rootDir, absolutePath)), reason: "max_files" });
5983
+ skipped.push({ path: toPosix(path12.relative(rootDir, absolutePath)), reason: "max_files" });
5633
5984
  continue;
5634
5985
  }
5635
5986
  files.push(absolutePath);
@@ -5651,12 +6002,12 @@ function resolveUrlMimeType(input, response) {
5651
6002
  function buildRemoteAssetRelativePath(assetUrl, mimeType) {
5652
6003
  const url = new URL(assetUrl);
5653
6004
  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);
6005
+ const extension = path12.posix.extname(normalized);
6006
+ const directory = path12.posix.dirname(normalized);
6007
+ const basename = extension ? path12.posix.basename(normalized, extension) : path12.posix.basename(normalized);
5657
6008
  const resolvedExtension = extension || `.${mime.extension(mimeType) || "bin"}`;
5658
6009
  const hashedName = `${basename || "asset"}-${sha256(assetUrl).slice(0, 8)}${resolvedExtension}`;
5659
- return directory === "." ? hashedName : path11.posix.join(directory, hashedName);
6010
+ return directory === "." ? hashedName : path12.posix.join(directory, hashedName);
5660
6011
  }
5661
6012
  async function readResponseBytesWithinLimit(response, maxBytes) {
5662
6013
  const contentLength = Number.parseInt(response.headers.get("content-length") ?? "", 10);
@@ -5809,34 +6160,34 @@ async function persistPreparedInput(rootDir, prepared, paths) {
5809
6160
  const previous = existingByOrigin ?? void 0;
5810
6161
  const sourceId = previous?.sourceId ?? `${slugify(prepared.title)}-${contentHash.slice(0, 8)}`;
5811
6162
  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);
6163
+ const storedPath = path12.join(paths.rawSourcesDir, `${sourceId}${prepared.storedExtension}`);
6164
+ const extractedTextPath = prepared.extractedText ? path12.join(paths.extractsDir, `${sourceId}.md`) : void 0;
6165
+ const extractedMetadataPath = prepared.extractionArtifact ? path12.join(paths.extractsDir, `${sourceId}.json`) : void 0;
6166
+ const attachmentsDir = path12.join(paths.rawAssetsDir, sourceId);
5816
6167
  if (previous?.storedPath) {
5817
- await fs10.rm(path11.resolve(rootDir, previous.storedPath), { force: true });
6168
+ await fs11.rm(path12.resolve(rootDir, previous.storedPath), { force: true });
5818
6169
  }
5819
6170
  if (previous?.extractedTextPath) {
5820
- await fs10.rm(path11.resolve(rootDir, previous.extractedTextPath), { force: true });
6171
+ await fs11.rm(path12.resolve(rootDir, previous.extractedTextPath), { force: true });
5821
6172
  }
5822
6173
  if (previous?.extractedMetadataPath) {
5823
- await fs10.rm(path11.resolve(rootDir, previous.extractedMetadataPath), { force: true });
6174
+ await fs11.rm(path12.resolve(rootDir, previous.extractedMetadataPath), { force: true });
5824
6175
  }
5825
- await fs10.rm(attachmentsDir, { recursive: true, force: true });
5826
- await fs10.writeFile(storedPath, prepared.payloadBytes);
6176
+ await fs11.rm(attachmentsDir, { recursive: true, force: true });
6177
+ await fs11.writeFile(storedPath, prepared.payloadBytes);
5827
6178
  if (prepared.extractedText && extractedTextPath) {
5828
- await fs10.writeFile(extractedTextPath, prepared.extractedText, "utf8");
6179
+ await fs11.writeFile(extractedTextPath, prepared.extractedText, "utf8");
5829
6180
  }
5830
6181
  if (prepared.extractionArtifact && extractedMetadataPath) {
5831
6182
  await writeJsonFile(extractedMetadataPath, prepared.extractionArtifact);
5832
6183
  }
5833
6184
  const manifestAttachments = [];
5834
6185
  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);
6186
+ const absoluteAttachmentPath = path12.join(attachmentsDir, attachment.relativePath);
6187
+ await ensureDir(path12.dirname(absoluteAttachmentPath));
6188
+ await fs11.writeFile(absoluteAttachmentPath, attachment.bytes);
5838
6189
  manifestAttachments.push({
5839
- path: toPosix(path11.relative(rootDir, absoluteAttachmentPath)),
6190
+ path: toPosix(path12.relative(rootDir, absoluteAttachmentPath)),
5840
6191
  mimeType: attachment.mimeType,
5841
6192
  originalPath: attachment.originalPath
5842
6193
  });
@@ -5852,9 +6203,9 @@ async function persistPreparedInput(rootDir, prepared, paths) {
5852
6203
  originalPath: prepared.originalPath,
5853
6204
  repoRelativePath: prepared.repoRelativePath,
5854
6205
  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,
6206
+ storedPath: toPosix(path12.relative(rootDir, storedPath)),
6207
+ extractedTextPath: extractedTextPath ? toPosix(path12.relative(rootDir, extractedTextPath)) : void 0,
6208
+ extractedMetadataPath: extractedMetadataPath ? toPosix(path12.relative(rootDir, extractedMetadataPath)) : void 0,
5858
6209
  extractionHash,
5859
6210
  mimeType: prepared.mimeType,
5860
6211
  contentHash,
@@ -5862,7 +6213,7 @@ async function persistPreparedInput(rootDir, prepared, paths) {
5862
6213
  updatedAt: now,
5863
6214
  attachments: manifestAttachments.length ? manifestAttachments : void 0
5864
6215
  };
5865
- await writeJsonFile(path11.join(paths.manifestsDir, `${sourceId}.json`), manifest);
6216
+ await writeJsonFile(path12.join(paths.manifestsDir, `${sourceId}.json`), manifest);
5866
6217
  await appendLogEntry(rootDir, "ingest", prepared.title, [
5867
6218
  `source_id=${sourceId}`,
5868
6219
  `kind=${prepared.sourceKind}`,
@@ -5880,16 +6231,16 @@ async function persistPreparedInput(rootDir, prepared, paths) {
5880
6231
  return { manifest, isNew: !previous, wasUpdated: Boolean(previous) };
5881
6232
  }
5882
6233
  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 });
6234
+ await fs11.rm(path12.join(paths.manifestsDir, `${manifest.sourceId}.json`), { force: true });
6235
+ await fs11.rm(path12.resolve(rootDir, manifest.storedPath), { force: true });
5885
6236
  if (manifest.extractedTextPath) {
5886
- await fs10.rm(path11.resolve(rootDir, manifest.extractedTextPath), { force: true });
6237
+ await fs11.rm(path12.resolve(rootDir, manifest.extractedTextPath), { force: true });
5887
6238
  }
5888
6239
  if (manifest.extractedMetadataPath) {
5889
- await fs10.rm(path11.resolve(rootDir, manifest.extractedMetadataPath), { force: true });
6240
+ await fs11.rm(path12.resolve(rootDir, manifest.extractedMetadataPath), { force: true });
5890
6241
  }
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 });
6242
+ await fs11.rm(path12.join(paths.rawAssetsDir, manifest.sourceId), { recursive: true, force: true });
6243
+ await fs11.rm(path12.join(paths.analysesDir, `${manifest.sourceId}.json`), { force: true });
5893
6244
  }
5894
6245
  function repoSyncWorkspaceIgnorePaths(rootDir, paths, repoRoot) {
5895
6246
  const candidates = [
@@ -5898,11 +6249,11 @@ function repoSyncWorkspaceIgnorePaths(rootDir, paths, repoRoot) {
5898
6249
  paths.stateDir,
5899
6250
  paths.agentDir,
5900
6251
  paths.inboxDir,
5901
- path11.join(rootDir, ".claude"),
5902
- path11.join(rootDir, ".cursor"),
5903
- path11.join(rootDir, ".obsidian")
6252
+ path12.join(rootDir, ".claude"),
6253
+ path12.join(rootDir, ".cursor"),
6254
+ path12.join(rootDir, ".obsidian")
5904
6255
  ];
5905
- return candidates.map((candidate) => path11.resolve(candidate)).filter((candidate, index, items) => items.indexOf(candidate) === index).filter((candidate) => withinRoot(repoRoot, candidate));
6256
+ return candidates.map((candidate) => path12.resolve(candidate)).filter((candidate, index, items) => items.indexOf(candidate) === index).filter((candidate) => withinRoot(repoRoot, candidate));
5906
6257
  }
5907
6258
  function preparedMatchesManifest(manifest, prepared, contentHash) {
5908
6259
  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 +6265,15 @@ function pendingSemanticRefreshId(changeType, repoRoot, relativePath) {
5914
6265
  return `pending:${changeType}:${sha256(`${toPosix(repoRoot)}:${relativePath}`).slice(0, 12)}`;
5915
6266
  }
5916
6267
  async function listTrackedRepoRoots(rootDir) {
6268
+ const managedSources = await readManagedSourcesIfPresent(rootDir).catch(() => null);
6269
+ if (managedSources && managedSources.length > 0) {
6270
+ const directoryRoots = managedSources.filter((source) => source.kind === "directory" && source.path).map((source) => path12.resolve(source.path));
6271
+ if (directoryRoots.length > 0) {
6272
+ return [...new Set(directoryRoots)].sort((left, right) => left.localeCompare(right));
6273
+ }
6274
+ }
5917
6275
  const manifests = await listManifests(rootDir);
5918
- return [...new Set(manifests.map((manifest) => repoRootFromManifest(manifest)).filter((item) => Boolean(item)))].sort(
6276
+ return [...new Set(manifests.map((manifest) => repoRootFromManifest2(manifest)).filter((item) => Boolean(item)))].sort(
5919
6277
  (left, right) => left.localeCompare(right)
5920
6278
  );
5921
6279
  }
@@ -5924,16 +6282,16 @@ async function syncTrackedRepos(rootDir, options, repoRoots) {
5924
6282
  const normalizedOptions = await resolveRepoIngestOptions(rootDir, options);
5925
6283
  const manifests = await listManifests(rootDir);
5926
6284
  const trackedRoots = (repoRoots && repoRoots.length > 0 ? repoRoots : await listTrackedRepoRoots(rootDir)).map(
5927
- (item) => path11.resolve(item)
6285
+ (item) => path12.resolve(item)
5928
6286
  );
5929
6287
  const uniqueRoots = [...new Set(trackedRoots)].sort((left, right) => left.localeCompare(right));
5930
6288
  const manifestsByRepoRoot = /* @__PURE__ */ new Map();
5931
6289
  for (const manifest of manifests) {
5932
- const repoRoot = repoRootFromManifest(manifest);
5933
- if (!repoRoot || !uniqueRoots.includes(path11.resolve(repoRoot))) {
6290
+ const repoRoot = repoRootFromManifest2(manifest);
6291
+ if (!repoRoot || !uniqueRoots.includes(path12.resolve(repoRoot))) {
5934
6292
  continue;
5935
6293
  }
5936
- const key = path11.resolve(repoRoot);
6294
+ const key = path12.resolve(repoRoot);
5937
6295
  const bucket = manifestsByRepoRoot.get(key) ?? [];
5938
6296
  bucket.push(manifest);
5939
6297
  manifestsByRepoRoot.set(key, bucket);
@@ -5958,15 +6316,15 @@ async function syncTrackedRepos(rootDir, options, repoRoots) {
5958
6316
  skipped.push(
5959
6317
  ...collected.skipped,
5960
6318
  ...collected.files.filter((absolutePath) => ignoreRoots.some((ignoreRoot) => withinRoot(ignoreRoot, absolutePath))).map((absolutePath) => ({
5961
- path: toPosix(path11.relative(rootDir, absolutePath)),
6319
+ path: toPosix(path12.relative(rootDir, absolutePath)),
5962
6320
  reason: "workspace_generated"
5963
6321
  }))
5964
6322
  );
5965
6323
  scannedCount += files.length;
5966
6324
  const progress = createProgressReporter("sync", files.length);
5967
- const currentPaths = new Set(files.map((absolutePath) => path11.resolve(absolutePath)));
6325
+ const currentPaths = new Set(files.map((absolutePath) => path12.resolve(absolutePath)));
5968
6326
  for (const absolutePath of files) {
5969
- const relativePath = repoRelativePathFor(absolutePath, repoRoot) ?? toPosix(path11.relative(repoRoot, absolutePath));
6327
+ const relativePath = repoRelativePathFor(absolutePath, repoRoot) ?? toPosix(path12.relative(repoRoot, absolutePath));
5970
6328
  const prepared = await prepareFileInput(rootDir, absolutePath, repoRoot, sourceClassForRelativePath(relativePath, normalizedOptions));
5971
6329
  const result = await persistPreparedInput(rootDir, prepared, paths);
5972
6330
  if (result.isNew) {
@@ -5976,9 +6334,9 @@ async function syncTrackedRepos(rootDir, options, repoRoots) {
5976
6334
  }
5977
6335
  progress.tick();
5978
6336
  }
5979
- progress.finish(`repo=${toPosix(path11.relative(rootDir, repoRoot)) || "."}`);
6337
+ progress.finish(`repo=${toPosix(path12.relative(rootDir, repoRoot)) || "."}`);
5980
6338
  for (const manifest of repoManifests) {
5981
- const originalPath = manifest.originalPath ? path11.resolve(manifest.originalPath) : null;
6339
+ const originalPath = manifest.originalPath ? path12.resolve(manifest.originalPath) : null;
5982
6340
  if (originalPath && !currentPaths.has(originalPath)) {
5983
6341
  await removeManifestArtifacts(rootDir, manifest, paths);
5984
6342
  removed.push(manifest);
@@ -5986,7 +6344,7 @@ async function syncTrackedRepos(rootDir, options, repoRoots) {
5986
6344
  }
5987
6345
  }
5988
6346
  if (uniqueRoots.length > 0) {
5989
- await appendLogEntry(rootDir, "sync_repo", uniqueRoots.map((repoRoot) => toPosix(path11.relative(rootDir, repoRoot)) || ".").join(","), [
6347
+ await appendLogEntry(rootDir, "sync_repo", uniqueRoots.map((repoRoot) => toPosix(path12.relative(rootDir, repoRoot)) || ".").join(","), [
5990
6348
  `repo_roots=${uniqueRoots.length}`,
5991
6349
  `scanned=${scannedCount}`,
5992
6350
  `imported=${imported.length}`,
@@ -6009,16 +6367,16 @@ async function syncTrackedReposForWatch(rootDir, options, repoRoots) {
6009
6367
  const normalizedOptions = await resolveRepoIngestOptions(rootDir, options);
6010
6368
  const manifests = await listManifests(rootDir);
6011
6369
  const trackedRoots = (repoRoots && repoRoots.length > 0 ? repoRoots : await listTrackedRepoRoots(rootDir)).map(
6012
- (item) => path11.resolve(item)
6370
+ (item) => path12.resolve(item)
6013
6371
  );
6014
6372
  const uniqueRoots = [...new Set(trackedRoots)].sort((left, right) => left.localeCompare(right));
6015
6373
  const manifestsByRepoRoot = /* @__PURE__ */ new Map();
6016
6374
  for (const manifest of manifests) {
6017
- const repoRoot = repoRootFromManifest(manifest);
6018
- if (!repoRoot || !uniqueRoots.includes(path11.resolve(repoRoot))) {
6375
+ const repoRoot = repoRootFromManifest2(manifest);
6376
+ if (!repoRoot || !uniqueRoots.includes(path12.resolve(repoRoot))) {
6019
6377
  continue;
6020
6378
  }
6021
- const key = path11.resolve(repoRoot);
6379
+ const key = path12.resolve(repoRoot);
6022
6380
  const bucket = manifestsByRepoRoot.get(key) ?? [];
6023
6381
  bucket.push(manifest);
6024
6382
  manifestsByRepoRoot.set(key, bucket);
@@ -6033,7 +6391,7 @@ async function syncTrackedReposForWatch(rootDir, options, repoRoots) {
6033
6391
  for (const repoRoot of uniqueRoots) {
6034
6392
  const repoManifests = manifestsByRepoRoot.get(repoRoot) ?? [];
6035
6393
  const manifestsByOriginalPath = new Map(
6036
- repoManifests.filter((manifest) => manifest.originalPath).map((manifest) => [path11.resolve(manifest.originalPath), manifest])
6394
+ repoManifests.filter((manifest) => manifest.originalPath).map((manifest) => [path12.resolve(manifest.originalPath), manifest])
6037
6395
  );
6038
6396
  if (!await fileExists(repoRoot)) {
6039
6397
  for (const manifest of repoManifests) {
@@ -6041,7 +6399,7 @@ async function syncTrackedReposForWatch(rootDir, options, repoRoots) {
6041
6399
  pendingSemanticRefresh.push({
6042
6400
  id: pendingSemanticRefreshId("removed", repoRoot, manifest.repoRelativePath ?? manifest.storedPath),
6043
6401
  repoRoot,
6044
- path: toPosix(path11.relative(rootDir, manifest.originalPath ?? manifest.storedPath)),
6402
+ path: toPosix(path12.relative(rootDir, manifest.originalPath ?? manifest.storedPath)),
6045
6403
  changeType: "removed",
6046
6404
  detectedAt: (/* @__PURE__ */ new Date()).toISOString(),
6047
6405
  sourceId: manifest.sourceId,
@@ -6061,18 +6419,18 @@ async function syncTrackedReposForWatch(rootDir, options, repoRoots) {
6061
6419
  skipped.push(
6062
6420
  ...collected.skipped,
6063
6421
  ...collected.files.filter((absolutePath) => ignoreRoots.some((ignoreRoot) => withinRoot(ignoreRoot, absolutePath))).map((absolutePath) => ({
6064
- path: toPosix(path11.relative(rootDir, absolutePath)),
6422
+ path: toPosix(path12.relative(rootDir, absolutePath)),
6065
6423
  reason: "workspace_generated"
6066
6424
  }))
6067
6425
  );
6068
6426
  scannedCount += files.length;
6069
6427
  const progress = createProgressReporter("sync-watch", files.length);
6070
- const currentPaths = new Set(files.map((absolutePath) => path11.resolve(absolutePath)));
6428
+ const currentPaths = new Set(files.map((absolutePath) => path12.resolve(absolutePath)));
6071
6429
  for (const absolutePath of files) {
6072
- const relativePath = repoRelativePathFor(absolutePath, repoRoot) ?? toPosix(path11.relative(repoRoot, absolutePath));
6430
+ const relativePath = repoRelativePathFor(absolutePath, repoRoot) ?? toPosix(path12.relative(repoRoot, absolutePath));
6073
6431
  const prepared = await prepareFileInput(rootDir, absolutePath, repoRoot, sourceClassForRelativePath(relativePath, normalizedOptions));
6074
6432
  if (shouldDeferWatchSemanticRefresh(prepared.sourceKind)) {
6075
- const existing = manifestsByOriginalPath.get(path11.resolve(absolutePath));
6433
+ const existing = manifestsByOriginalPath.get(path12.resolve(absolutePath));
6076
6434
  const contentHash = buildCompositeHash(prepared.payloadBytes, prepared.attachments);
6077
6435
  const changed = !existing || !preparedMatchesManifest(existing, prepared, contentHash);
6078
6436
  if (changed) {
@@ -6080,10 +6438,10 @@ async function syncTrackedReposForWatch(rootDir, options, repoRoots) {
6080
6438
  id: pendingSemanticRefreshId(
6081
6439
  existing ? "modified" : "added",
6082
6440
  repoRoot,
6083
- prepared.repoRelativePath ?? toPosix(path11.relative(repoRoot, absolutePath))
6441
+ prepared.repoRelativePath ?? toPosix(path12.relative(repoRoot, absolutePath))
6084
6442
  ),
6085
6443
  repoRoot,
6086
- path: toPosix(path11.relative(rootDir, absolutePath)),
6444
+ path: toPosix(path12.relative(rootDir, absolutePath)),
6087
6445
  changeType: existing ? "modified" : "added",
6088
6446
  detectedAt: (/* @__PURE__ */ new Date()).toISOString(),
6089
6447
  sourceId: existing?.sourceId,
@@ -6104,15 +6462,15 @@ async function syncTrackedReposForWatch(rootDir, options, repoRoots) {
6104
6462
  }
6105
6463
  progress.tick();
6106
6464
  }
6107
- progress.finish(`repo=${toPosix(path11.relative(rootDir, repoRoot)) || "."}`);
6465
+ progress.finish(`repo=${toPosix(path12.relative(rootDir, repoRoot)) || "."}`);
6108
6466
  for (const manifest of repoManifests) {
6109
- const originalPath = manifest.originalPath ? path11.resolve(manifest.originalPath) : null;
6467
+ const originalPath = manifest.originalPath ? path12.resolve(manifest.originalPath) : null;
6110
6468
  if (originalPath && !currentPaths.has(originalPath)) {
6111
6469
  if (shouldDeferWatchSemanticRefresh(manifest.sourceKind)) {
6112
6470
  pendingSemanticRefresh.push({
6113
- id: pendingSemanticRefreshId("removed", repoRoot, manifest.repoRelativePath ?? toPosix(path11.relative(repoRoot, originalPath))),
6471
+ id: pendingSemanticRefreshId("removed", repoRoot, manifest.repoRelativePath ?? toPosix(path12.relative(repoRoot, originalPath))),
6114
6472
  repoRoot,
6115
- path: toPosix(path11.relative(rootDir, originalPath)),
6473
+ path: toPosix(path12.relative(rootDir, originalPath)),
6116
6474
  changeType: "removed",
6117
6475
  detectedAt: (/* @__PURE__ */ new Date()).toISOString(),
6118
6476
  sourceId: manifest.sourceId,
@@ -6130,7 +6488,7 @@ async function syncTrackedReposForWatch(rootDir, options, repoRoots) {
6130
6488
  await appendLogEntry(
6131
6489
  rootDir,
6132
6490
  "sync_repo_watch",
6133
- uniqueRoots.map((repoRoot) => toPosix(path11.relative(rootDir, repoRoot)) || ".").join(","),
6491
+ uniqueRoots.map((repoRoot) => toPosix(path12.relative(rootDir, repoRoot)) || ".").join(","),
6134
6492
  [
6135
6493
  `repo_roots=${uniqueRoots.length}`,
6136
6494
  `scanned=${scannedCount}`,
@@ -6156,17 +6514,17 @@ async function syncTrackedReposForWatch(rootDir, options, repoRoots) {
6156
6514
  };
6157
6515
  }
6158
6516
  async function prepareFileInput(rootDir, absoluteInput, repoRoot, sourceClass) {
6159
- const payloadBytes = await fs10.readFile(absoluteInput);
6517
+ const payloadBytes = await fs11.readFile(absoluteInput);
6160
6518
  const mimeType = guessMimeType(absoluteInput);
6161
6519
  const sourceKind = inferKind(mimeType, absoluteInput);
6162
6520
  const language = inferCodeLanguage(absoluteInput, mimeType);
6163
- const storedExtension = path11.extname(absoluteInput) || `.${mime.extension(mimeType) || "bin"}`;
6521
+ const storedExtension = path12.extname(absoluteInput) || `.${mime.extension(mimeType) || "bin"}`;
6164
6522
  let title;
6165
6523
  let extractedText;
6166
6524
  let extractionArtifact;
6167
6525
  if (sourceKind === "markdown" || sourceKind === "text" || sourceKind === "code") {
6168
6526
  extractedText = extractedTextForPlainSource(absoluteInput, sourceKind, payloadBytes.toString("utf8"));
6169
- title = titleFromText(path11.basename(absoluteInput, path11.extname(absoluteInput)), extractedText, absoluteInput);
6527
+ title = titleFromText(path12.basename(absoluteInput, path12.extname(absoluteInput)), extractedText, absoluteInput);
6170
6528
  extractionArtifact = createPlainTextExtractionArtifact(sourceKind, mimeType);
6171
6529
  } else if (sourceKind === "html") {
6172
6530
  const html = payloadBytes.toString("utf8");
@@ -6175,18 +6533,18 @@ async function prepareFileInput(rootDir, absoluteInput, repoRoot, sourceClass) {
6175
6533
  extractedText = converted.markdown;
6176
6534
  extractionArtifact = createHtmlReadabilityExtractionArtifact(sourceKind, mimeType);
6177
6535
  } else if (sourceKind === "pdf") {
6178
- title = path11.basename(absoluteInput, path11.extname(absoluteInput));
6536
+ title = path12.basename(absoluteInput, path12.extname(absoluteInput));
6179
6537
  const extracted = await extractPdfText({ mimeType, bytes: payloadBytes });
6180
6538
  extractedText = extracted.extractedText;
6181
6539
  extractionArtifact = extracted.artifact;
6182
6540
  } else if (sourceKind === "docx") {
6183
- title = path11.basename(absoluteInput, path11.extname(absoluteInput));
6541
+ title = path12.basename(absoluteInput, path12.extname(absoluteInput));
6184
6542
  const extracted = await extractDocxText({ mimeType, bytes: payloadBytes });
6185
6543
  title = extracted.artifact.metadata?.title?.trim() || title;
6186
6544
  extractedText = extracted.extractedText;
6187
6545
  extractionArtifact = extracted.artifact;
6188
6546
  } else if (sourceKind === "image") {
6189
- title = path11.basename(absoluteInput, path11.extname(absoluteInput));
6547
+ title = path12.basename(absoluteInput, path12.extname(absoluteInput));
6190
6548
  const extracted = await extractImageWithVision(rootDir, {
6191
6549
  title,
6192
6550
  mimeType,
@@ -6196,7 +6554,7 @@ async function prepareFileInput(rootDir, absoluteInput, repoRoot, sourceClass) {
6196
6554
  extractedText = extracted.extractedText;
6197
6555
  extractionArtifact = extracted.artifact;
6198
6556
  } else {
6199
- title = path11.basename(absoluteInput, path11.extname(absoluteInput));
6557
+ title = path12.basename(absoluteInput, path12.extname(absoluteInput));
6200
6558
  }
6201
6559
  return {
6202
6560
  title,
@@ -6273,7 +6631,7 @@ async function prepareUrlInput(rootDir, input, options) {
6273
6631
  sourceKind = "markdown";
6274
6632
  storedExtension = ".md";
6275
6633
  } else {
6276
- const extension = path11.extname(inputUrl.pathname);
6634
+ const extension = path12.extname(inputUrl.pathname);
6277
6635
  storedExtension = extension || `.${mime.extension(mimeType) || "bin"}`;
6278
6636
  if (sourceKind === "markdown" || sourceKind === "text" || sourceKind === "code") {
6279
6637
  extractedText = extractedTextForPlainSource(inputUrl.pathname, sourceKind, payloadBytes.toString("utf8"));
@@ -6344,14 +6702,14 @@ async function collectInboxAttachmentRefs(inputDir, files) {
6344
6702
  if (sourceKind !== "markdown" && sourceKind !== "html") {
6345
6703
  continue;
6346
6704
  }
6347
- const content = await fs10.readFile(absolutePath, "utf8");
6705
+ const content = await fs11.readFile(absolutePath, "utf8");
6348
6706
  const refs = sourceKind === "html" ? extractHtmlLocalReferences(content, pathToFileURL(absolutePath).toString()) : extractMarkdownReferences(content);
6349
6707
  if (!refs.length) {
6350
6708
  continue;
6351
6709
  }
6352
6710
  const sourceRefs = [];
6353
6711
  for (const ref of refs) {
6354
- const resolved = path11.resolve(path11.dirname(absolutePath), ref);
6712
+ const resolved = path12.resolve(path12.dirname(absolutePath), ref);
6355
6713
  if (!resolved.startsWith(inputDir) || !await fileExists(resolved)) {
6356
6714
  continue;
6357
6715
  }
@@ -6385,12 +6743,12 @@ function rewriteMarkdownReferences(content, replacements) {
6385
6743
  });
6386
6744
  }
6387
6745
  async function prepareInboxMarkdownInput(absolutePath, attachmentRefs) {
6388
- const originalBytes = await fs10.readFile(absolutePath);
6746
+ const originalBytes = await fs11.readFile(absolutePath);
6389
6747
  const originalText = originalBytes.toString("utf8");
6390
- const title = titleFromText(path11.basename(absolutePath, path11.extname(absolutePath)), originalText);
6748
+ const title = titleFromText(path12.basename(absolutePath, path12.extname(absolutePath)), originalText);
6391
6749
  const attachments = [];
6392
6750
  for (const attachmentRef of attachmentRefs) {
6393
- const bytes = await fs10.readFile(attachmentRef.absolutePath);
6751
+ const bytes = await fs11.readFile(attachmentRef.absolutePath);
6394
6752
  attachments.push({
6395
6753
  relativePath: sanitizeAssetRelativePath(attachmentRef.relativeRef),
6396
6754
  mimeType: guessMimeType(attachmentRef.absolutePath),
@@ -6414,7 +6772,7 @@ async function prepareInboxMarkdownInput(absolutePath, attachmentRefs) {
6414
6772
  sourceKind: "markdown",
6415
6773
  originalPath: toPosix(absolutePath),
6416
6774
  mimeType: "text/markdown",
6417
- storedExtension: path11.extname(absolutePath) || ".md",
6775
+ storedExtension: path12.extname(absolutePath) || ".md",
6418
6776
  payloadBytes: Buffer.from(rewrittenText, "utf8"),
6419
6777
  extractedText: rewrittenText,
6420
6778
  extractionArtifact,
@@ -6424,12 +6782,12 @@ async function prepareInboxMarkdownInput(absolutePath, attachmentRefs) {
6424
6782
  };
6425
6783
  }
6426
6784
  async function prepareInboxHtmlInput(absolutePath, attachmentRefs) {
6427
- const originalBytes = await fs10.readFile(absolutePath);
6785
+ const originalBytes = await fs11.readFile(absolutePath);
6428
6786
  const originalHtml = originalBytes.toString("utf8");
6429
6787
  const initialConversion = await convertHtmlToMarkdown(originalHtml, pathToFileURL(absolutePath).toString());
6430
6788
  const attachments = [];
6431
6789
  for (const attachmentRef of attachmentRefs) {
6432
- const bytes = await fs10.readFile(attachmentRef.absolutePath);
6790
+ const bytes = await fs11.readFile(attachmentRef.absolutePath);
6433
6791
  attachments.push({
6434
6792
  relativePath: sanitizeAssetRelativePath(attachmentRef.relativeRef),
6435
6793
  mimeType: guessMimeType(attachmentRef.absolutePath),
@@ -6438,7 +6796,7 @@ async function prepareInboxHtmlInput(absolutePath, attachmentRefs) {
6438
6796
  });
6439
6797
  }
6440
6798
  const contentHash = buildCompositeHash(originalBytes, attachments);
6441
- const fallbackTitle = path11.basename(absolutePath, path11.extname(absolutePath));
6799
+ const fallbackTitle = path12.basename(absolutePath, path12.extname(absolutePath));
6442
6800
  const title = initialConversion.title || fallbackTitle;
6443
6801
  const sourceId = `${slugify(title)}-${contentHash.slice(0, 8)}`;
6444
6802
  const replacements = new Map(
@@ -6456,7 +6814,7 @@ async function prepareInboxHtmlInput(absolutePath, attachmentRefs) {
6456
6814
  sourceKind: "html",
6457
6815
  originalPath: toPosix(absolutePath),
6458
6816
  mimeType: "text/html",
6459
- storedExtension: path11.extname(absolutePath) || ".html",
6817
+ storedExtension: path12.extname(absolutePath) || ".html",
6460
6818
  payloadBytes: Buffer.from(rewrittenHtml, "utf8"),
6461
6819
  extractedText: converted.markdown,
6462
6820
  extractionArtifact,
@@ -6468,14 +6826,16 @@ async function prepareInboxHtmlInput(absolutePath, attachmentRefs) {
6468
6826
  function isSupportedInboxKind(sourceKind) {
6469
6827
  return ["markdown", "text", "html", "pdf", "docx", "image"].includes(sourceKind);
6470
6828
  }
6471
- async function ingestInput(rootDir, input, options) {
6829
+ async function ingestInputDetailed(rootDir, input, options) {
6472
6830
  const { paths } = await initWorkspace(rootDir);
6473
6831
  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));
6832
+ const absoluteInput = path12.resolve(rootDir, input);
6833
+ const repoRoot = isHttpUrl(input) || normalizedOptions.repoRoot ? normalizedOptions.repoRoot : await findNearestGitRoot2(absoluteInput).then((value) => value ?? path12.dirname(absoluteInput));
6476
6834
  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;
6835
+ return await persistPreparedInput(rootDir, prepared, paths);
6836
+ }
6837
+ async function ingestInput(rootDir, input, options) {
6838
+ return (await ingestInputDetailed(rootDir, input, options)).manifest;
6479
6839
  }
6480
6840
  async function addInput(rootDir, input, options = {}) {
6481
6841
  const { paths } = await initWorkspace(rootDir);
@@ -6562,7 +6922,7 @@ async function addInput(rootDir, input, options = {}) {
6562
6922
  async function ingestDirectory(rootDir, inputDir, options) {
6563
6923
  const { paths } = await initWorkspace(rootDir);
6564
6924
  const normalizedOptions = await resolveRepoIngestOptions(rootDir, options);
6565
- const absoluteInputDir = path11.resolve(rootDir, inputDir);
6925
+ const absoluteInputDir = path12.resolve(rootDir, inputDir);
6566
6926
  const repoRoot = normalizedOptions.repoRoot ?? await findNearestGitRoot2(absoluteInputDir) ?? absoluteInputDir;
6567
6927
  if (!await fileExists(absoluteInputDir)) {
6568
6928
  throw new Error(`Directory not found: ${absoluteInputDir}`);
@@ -6572,7 +6932,7 @@ async function ingestDirectory(rootDir, inputDir, options) {
6572
6932
  const updated = [];
6573
6933
  const progress = createProgressReporter("ingest", files.length);
6574
6934
  for (const absolutePath of files) {
6575
- const relativePath = repoRelativePathFor(absolutePath, repoRoot) ?? toPosix(path11.relative(repoRoot, absolutePath));
6935
+ const relativePath = repoRelativePathFor(absolutePath, repoRoot) ?? toPosix(path12.relative(repoRoot, absolutePath));
6576
6936
  const prepared = await prepareFileInput(rootDir, absolutePath, repoRoot, sourceClassForRelativePath(relativePath, normalizedOptions));
6577
6937
  const result = await persistPreparedInput(rootDir, prepared, paths);
6578
6938
  if (result.isNew) {
@@ -6580,13 +6940,13 @@ async function ingestDirectory(rootDir, inputDir, options) {
6580
6940
  } else if (result.wasUpdated) {
6581
6941
  updated.push(result.manifest);
6582
6942
  } else {
6583
- skipped.push({ path: toPosix(path11.relative(rootDir, absolutePath)), reason: "duplicate_content" });
6943
+ skipped.push({ path: toPosix(path12.relative(rootDir, absolutePath)), reason: "duplicate_content" });
6584
6944
  }
6585
6945
  progress.tick();
6586
6946
  }
6587
6947
  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)) || "."}`,
6948
+ await appendLogEntry(rootDir, "ingest_directory", toPosix(path12.relative(rootDir, absoluteInputDir)) || ".", [
6949
+ `repo_root=${toPosix(path12.relative(rootDir, repoRoot)) || "."}`,
6590
6950
  `scanned=${files.length}`,
6591
6951
  `imported=${imported.length}`,
6592
6952
  `updated=${updated.length}`,
@@ -6603,7 +6963,7 @@ async function ingestDirectory(rootDir, inputDir, options) {
6603
6963
  }
6604
6964
  async function importInbox(rootDir, inputDir) {
6605
6965
  const { paths } = await initWorkspace(rootDir);
6606
- const effectiveInputDir = path11.resolve(rootDir, inputDir ?? paths.inboxDir);
6966
+ const effectiveInputDir = path12.resolve(rootDir, inputDir ?? paths.inboxDir);
6607
6967
  if (!await fileExists(effectiveInputDir)) {
6608
6968
  throw new Error(`Inbox directory not found: ${effectiveInputDir}`);
6609
6969
  }
@@ -6614,31 +6974,31 @@ async function importInbox(rootDir, inputDir) {
6614
6974
  const skipped = [];
6615
6975
  let attachmentCount = 0;
6616
6976
  for (const absolutePath of files) {
6617
- const basename = path11.basename(absolutePath);
6977
+ const basename = path12.basename(absolutePath);
6618
6978
  if (basename.startsWith(".")) {
6619
- skipped.push({ path: toPosix(path11.relative(rootDir, absolutePath)), reason: "hidden_file" });
6979
+ skipped.push({ path: toPosix(path12.relative(rootDir, absolutePath)), reason: "hidden_file" });
6620
6980
  continue;
6621
6981
  }
6622
6982
  if (claimedAttachments.has(absolutePath)) {
6623
- skipped.push({ path: toPosix(path11.relative(rootDir, absolutePath)), reason: "referenced_attachment" });
6983
+ skipped.push({ path: toPosix(path12.relative(rootDir, absolutePath)), reason: "referenced_attachment" });
6624
6984
  continue;
6625
6985
  }
6626
6986
  const mimeType = guessMimeType(absolutePath);
6627
6987
  const sourceKind = inferKind(mimeType, absolutePath);
6628
6988
  if (!isSupportedInboxKind(sourceKind)) {
6629
- skipped.push({ path: toPosix(path11.relative(rootDir, absolutePath)), reason: `unsupported_kind:${sourceKind}` });
6989
+ skipped.push({ path: toPosix(path12.relative(rootDir, absolutePath)), reason: `unsupported_kind:${sourceKind}` });
6630
6990
  continue;
6631
6991
  }
6632
6992
  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
6993
  const result = await persistPreparedInput(rootDir, prepared, paths);
6634
6994
  if (!result.isNew) {
6635
- skipped.push({ path: toPosix(path11.relative(rootDir, absolutePath)), reason: "duplicate_content" });
6995
+ skipped.push({ path: toPosix(path12.relative(rootDir, absolutePath)), reason: "duplicate_content" });
6636
6996
  continue;
6637
6997
  }
6638
6998
  attachmentCount += result.manifest.attachments?.length ?? 0;
6639
6999
  imported.push(result.manifest);
6640
7000
  }
6641
- await appendLogEntry(rootDir, "inbox_import", toPosix(path11.relative(rootDir, effectiveInputDir)) || ".", [
7001
+ await appendLogEntry(rootDir, "inbox_import", toPosix(path12.relative(rootDir, effectiveInputDir)) || ".", [
6642
7002
  `scanned=${files.length}`,
6643
7003
  `imported=${imported.length}`,
6644
7004
  `attachments=${attachmentCount}`,
@@ -6657,27 +7017,36 @@ async function listManifests(rootDir) {
6657
7017
  if (!await fileExists(paths.manifestsDir)) {
6658
7018
  return [];
6659
7019
  }
6660
- const entries = await fs10.readdir(paths.manifestsDir);
7020
+ const entries = await fs11.readdir(paths.manifestsDir);
6661
7021
  const manifests = await Promise.all(
6662
- entries.filter((entry) => entry.endsWith(".json")).map((entry) => readJsonFile(path11.join(paths.manifestsDir, entry)))
7022
+ entries.filter((entry) => entry.endsWith(".json")).map((entry) => readJsonFile(path12.join(paths.manifestsDir, entry)))
6663
7023
  );
6664
7024
  return manifests.filter((manifest) => Boolean(manifest));
6665
7025
  }
7026
+ async function removeManifestBySourceId(rootDir, sourceId) {
7027
+ const { paths } = await initWorkspace(rootDir);
7028
+ const manifest = await readJsonFile(path12.join(paths.manifestsDir, `${sourceId}.json`));
7029
+ if (!manifest) {
7030
+ return null;
7031
+ }
7032
+ await removeManifestArtifacts(rootDir, manifest, paths);
7033
+ return manifest;
7034
+ }
6666
7035
  async function readExtractedText(rootDir, manifest) {
6667
7036
  if (!manifest.extractedTextPath) {
6668
7037
  return void 0;
6669
7038
  }
6670
- const absolutePath = path11.resolve(rootDir, manifest.extractedTextPath);
7039
+ const absolutePath = path12.resolve(rootDir, manifest.extractedTextPath);
6671
7040
  if (!await fileExists(absolutePath)) {
6672
7041
  return void 0;
6673
7042
  }
6674
- return fs10.readFile(absolutePath, "utf8");
7043
+ return fs11.readFile(absolutePath, "utf8");
6675
7044
  }
6676
7045
  async function readExtractionArtifact(rootDir, manifest) {
6677
7046
  if (!manifest.extractedMetadataPath) {
6678
7047
  return void 0;
6679
7048
  }
6680
- const absolutePath = path11.resolve(rootDir, manifest.extractedMetadataPath);
7049
+ const absolutePath = path12.resolve(rootDir, manifest.extractedMetadataPath);
6681
7050
  if (!await fileExists(absolutePath)) {
6682
7051
  return void 0;
6683
7052
  }
@@ -6685,20 +7054,20 @@ async function readExtractionArtifact(rootDir, manifest) {
6685
7054
  }
6686
7055
 
6687
7056
  // src/mcp.ts
6688
- import fs18 from "fs/promises";
6689
- import path22 from "path";
7057
+ import fs19 from "fs/promises";
7058
+ import path23 from "path";
6690
7059
  import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
6691
7060
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
6692
7061
  import { z as z8 } from "zod";
6693
7062
 
6694
7063
  // src/schema.ts
6695
- import fs11 from "fs/promises";
6696
- import path12 from "path";
7064
+ import fs12 from "fs/promises";
7065
+ import path13 from "path";
6697
7066
  function normalizeSchemaContent(content) {
6698
7067
  return content.trim() ? content.trim() : defaultVaultSchema().trim();
6699
7068
  }
6700
7069
  async function readSchemaFile(schemaPath, fallback = defaultVaultSchema()) {
6701
- const content = await fileExists(schemaPath) ? await fs11.readFile(schemaPath, "utf8") : fallback;
7070
+ const content = await fileExists(schemaPath) ? await fs12.readFile(schemaPath, "utf8") : fallback;
6702
7071
  const normalized = normalizeSchemaContent(content);
6703
7072
  return {
6704
7073
  path: schemaPath,
@@ -6707,7 +7076,7 @@ async function readSchemaFile(schemaPath, fallback = defaultVaultSchema()) {
6707
7076
  };
6708
7077
  }
6709
7078
  function resolveProjectSchemaPath(rootDir, schemaPath) {
6710
- return path12.resolve(rootDir, schemaPath);
7079
+ return path13.resolve(rootDir, schemaPath);
6711
7080
  }
6712
7081
  function composeVaultSchema(root, projectSchemas = []) {
6713
7082
  if (!projectSchemas.length) {
@@ -6723,7 +7092,7 @@ function composeVaultSchema(root, projectSchemas = []) {
6723
7092
  (schema) => [
6724
7093
  `## Project Schema`,
6725
7094
  "",
6726
- `Path: ${toPosix(path12.relative(path12.dirname(root.path), schema.path) || schema.path)}`,
7095
+ `Path: ${toPosix(path13.relative(path13.dirname(root.path), schema.path) || schema.path)}`,
6727
7096
  "",
6728
7097
  schema.content
6729
7098
  ].join("\n")
@@ -6799,13 +7168,13 @@ function buildSchemaPrompt(schema, instruction) {
6799
7168
  }
6800
7169
 
6801
7170
  // src/vault.ts
6802
- import fs17 from "fs/promises";
6803
- import path21 from "path";
7171
+ import fs18 from "fs/promises";
7172
+ import path22 from "path";
6804
7173
  import matter9 from "gray-matter";
6805
7174
  import { z as z7 } from "zod";
6806
7175
 
6807
7176
  // src/analysis.ts
6808
- import path13 from "path";
7177
+ import path14 from "path";
6809
7178
  import { z as z2 } from "zod";
6810
7179
  var ANALYSIS_FORMAT_VERSION = 6;
6811
7180
  var sourceAnalysisSchema = z2.object({
@@ -7034,7 +7403,7 @@ function extractionWarningSummary(manifest, extraction) {
7034
7403
  return `Imported ${manifest.sourceKind} source. Text extraction is not yet available for this source.`;
7035
7404
  }
7036
7405
  async function analyzeSource(manifest, extractedText, provider, paths, schema) {
7037
- const cachePath = path13.join(paths.analysesDir, `${manifest.sourceId}.json`);
7406
+ const cachePath = path14.join(paths.analysesDir, `${manifest.sourceId}.json`);
7038
7407
  const cached = await readJsonFile(cachePath);
7039
7408
  if (cached && cached.analysisVersion === ANALYSIS_FORMAT_VERSION && cached.sourceHash === manifest.contentHash && cached.extractionHash === manifest.extractionHash && cached.schemaHash === schema.hash) {
7040
7409
  return cached;
@@ -7124,8 +7493,8 @@ function conflictConfidence(claimA, claimB) {
7124
7493
  }
7125
7494
 
7126
7495
  // src/deep-lint.ts
7127
- import fs12 from "fs/promises";
7128
- import path16 from "path";
7496
+ import fs13 from "fs/promises";
7497
+ import path17 from "path";
7129
7498
  import matter4 from "gray-matter";
7130
7499
  import { z as z5 } from "zod";
7131
7500
 
@@ -7146,7 +7515,7 @@ function normalizeFindingSeverity(value) {
7146
7515
 
7147
7516
  // src/orchestration.ts
7148
7517
  import { spawn } from "child_process";
7149
- import path14 from "path";
7518
+ import path15 from "path";
7150
7519
  import { z as z3 } from "zod";
7151
7520
  var orchestrationRoleResultSchema = z3.object({
7152
7521
  summary: z3.string().optional(),
@@ -7239,7 +7608,7 @@ async function runProviderRole(rootDir, role, roleConfig, input) {
7239
7608
  }
7240
7609
  async function runCommandRole(rootDir, role, executor, input) {
7241
7610
  const [command, ...args] = executor.command;
7242
- const cwd = executor.cwd ? path14.resolve(rootDir, executor.cwd) : rootDir;
7611
+ const cwd = executor.cwd ? path15.resolve(rootDir, executor.cwd) : rootDir;
7243
7612
  const child = spawn(command, args, {
7244
7613
  cwd,
7245
7614
  env: {
@@ -7333,7 +7702,7 @@ function summarizeRoleQuestions(results) {
7333
7702
  }
7334
7703
 
7335
7704
  // src/web-search/registry.ts
7336
- import path15 from "path";
7705
+ import path16 from "path";
7337
7706
  import { pathToFileURL as pathToFileURL2 } from "url";
7338
7707
  import { z as z4 } from "zod";
7339
7708
 
@@ -7431,7 +7800,7 @@ async function createWebSearchAdapter(id, config, rootDir) {
7431
7800
  if (!config.module) {
7432
7801
  throw new Error(`Web search provider ${id} is type "custom" but no module path was configured.`);
7433
7802
  }
7434
- const resolvedModule = path15.isAbsolute(config.module) ? config.module : path15.resolve(rootDir, config.module);
7803
+ const resolvedModule = path16.isAbsolute(config.module) ? config.module : path16.resolve(rootDir, config.module);
7435
7804
  const loaded = await import(pathToFileURL2(resolvedModule).href);
7436
7805
  const parsed = customWebSearchModuleSchema.parse(loaded);
7437
7806
  return parsed.createAdapter(id, config, rootDir);
@@ -7498,8 +7867,8 @@ async function loadContextPages(rootDir, graph) {
7498
7867
  );
7499
7868
  return Promise.all(
7500
7869
  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(() => "");
7870
+ const absolutePath = path17.join(paths.wikiDir, page.path);
7871
+ const raw = await fs13.readFile(absolutePath, "utf8").catch(() => "");
7503
7872
  const parsed = matter4(raw);
7504
7873
  return {
7505
7874
  id: page.id,
@@ -7547,7 +7916,7 @@ function heuristicDeepFindings(contextPages, structuralFindings, graph) {
7547
7916
  code: "missing_citation",
7548
7917
  message: finding.message,
7549
7918
  pagePath: finding.pagePath,
7550
- suggestedQuery: finding.pagePath ? `Which sources support the claims in ${path16.basename(finding.pagePath, ".md")}?` : void 0
7919
+ suggestedQuery: finding.pagePath ? `Which sources support the claims in ${path17.basename(finding.pagePath, ".md")}?` : void 0
7551
7920
  });
7552
7921
  }
7553
7922
  for (const page of contextPages.filter((item) => item.kind === "source").slice(0, 3)) {
@@ -7726,8 +8095,8 @@ async function runDeepLint(rootDir, structuralFindings, options = {}) {
7726
8095
  }
7727
8096
 
7728
8097
  // src/embeddings.ts
7729
- import fs13 from "fs/promises";
7730
- import path17 from "path";
8098
+ import fs14 from "fs/promises";
8099
+ import path18 from "path";
7731
8100
  var MAX_EMBEDDING_BATCH = 32;
7732
8101
  var MAX_SIMILARITY_NODES = 240;
7733
8102
  function cosineSimilarity(left, right) {
@@ -7761,8 +8130,8 @@ async function loadPageContents(rootDir, graph) {
7761
8130
  const contents = /* @__PURE__ */ new Map();
7762
8131
  await Promise.all(
7763
8132
  graph.pages.map(async (page) => {
7764
- const absolutePath = path17.join(paths.wikiDir, page.path);
7765
- const content = await fs13.readFile(absolutePath, "utf8").catch(() => {
8133
+ const absolutePath = path18.join(paths.wikiDir, page.path);
8134
+ const content = await fs14.readFile(absolutePath, "utf8").catch(() => {
7766
8135
  process.stderr.write(`[swarmvault] Warning: could not read page ${page.path} for embedding
7767
8136
  `);
7768
8137
  return "";
@@ -9264,15 +9633,15 @@ function sourceTypeForNode(node, pagesById) {
9264
9633
  return pagesById.get(node.pageId)?.sourceType;
9265
9634
  }
9266
9635
  function supportingPathDetails(graph, edge) {
9267
- const path26 = shortestGraphPath(graph, edge.source, edge.target);
9636
+ const path28 = shortestGraphPath(graph, edge.source, edge.target);
9268
9637
  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));
9638
+ const pathEdges = path28.edgeIds.map((edgeId) => edgesById.get(edgeId)).filter((item) => Boolean(item));
9270
9639
  return {
9271
- pathNodeIds: path26.nodeIds,
9272
- pathEdgeIds: path26.edgeIds,
9640
+ pathNodeIds: path28.nodeIds,
9641
+ pathEdgeIds: path28.edgeIds,
9273
9642
  pathRelations: pathEdges.map((item) => item.relation),
9274
9643
  pathEvidenceClasses: pathEdges.map((item) => item.evidenceClass),
9275
- pathSummary: path26.summary
9644
+ pathSummary: path28.summary
9276
9645
  };
9277
9646
  }
9278
9647
  function surpriseScore(edge, graph, pagesById, hyperedgesByNodeId) {
@@ -9341,7 +9710,7 @@ function topSurprisingConnections(graph, pagesById) {
9341
9710
  }).map((edge) => {
9342
9711
  const source = nodesById.get(edge.source);
9343
9712
  const target = nodesById.get(edge.target);
9344
- const path26 = supportingPathDetails(graph, edge);
9713
+ const path28 = supportingPathDetails(graph, edge);
9345
9714
  const scored = surpriseScore(edge, graph, pagesById, hyperedgesByNodeId);
9346
9715
  return {
9347
9716
  id: edge.id,
@@ -9352,11 +9721,11 @@ function topSurprisingConnections(graph, pagesById) {
9352
9721
  relation: edge.relation,
9353
9722
  evidenceClass: edge.evidenceClass,
9354
9723
  confidence: edge.confidence,
9355
- pathNodeIds: path26.pathNodeIds,
9356
- pathEdgeIds: path26.pathEdgeIds,
9357
- pathRelations: path26.pathRelations,
9358
- pathEvidenceClasses: path26.pathEvidenceClasses,
9359
- pathSummary: path26.pathSummary,
9724
+ pathNodeIds: path28.pathNodeIds,
9725
+ pathEdgeIds: path28.pathEdgeIds,
9726
+ pathRelations: path28.pathRelations,
9727
+ pathEvidenceClasses: path28.pathEvidenceClasses,
9728
+ pathSummary: path28.pathSummary,
9360
9729
  why: scored.why,
9361
9730
  explanation: scored.explanation,
9362
9731
  surpriseScore: scored.score
@@ -10283,13 +10652,13 @@ function buildOutputAssetManifest(input) {
10283
10652
  }
10284
10653
 
10285
10654
  // src/outputs.ts
10286
- import fs15 from "fs/promises";
10287
- import path19 from "path";
10655
+ import fs16 from "fs/promises";
10656
+ import path20 from "path";
10288
10657
  import matter7 from "gray-matter";
10289
10658
 
10290
10659
  // src/pages.ts
10291
- import fs14 from "fs/promises";
10292
- import path18 from "path";
10660
+ import fs15 from "fs/promises";
10661
+ import path19 from "path";
10293
10662
  import matter6 from "gray-matter";
10294
10663
  function normalizeStringArray(value) {
10295
10664
  return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
@@ -10367,7 +10736,7 @@ async function loadExistingManagedPageState(absolutePath, defaults = {}) {
10367
10736
  updatedAt: updatedFallback
10368
10737
  };
10369
10738
  }
10370
- const content = await fs14.readFile(absolutePath, "utf8");
10739
+ const content = await fs15.readFile(absolutePath, "utf8");
10371
10740
  const parsed = matter6(content);
10372
10741
  return {
10373
10742
  status: normalizePageStatus(parsed.data.status, defaults.status ?? "active"),
@@ -10406,7 +10775,7 @@ function parseStoredPage(relativePath, content, defaults = {}) {
10406
10775
  const now = (/* @__PURE__ */ new Date()).toISOString();
10407
10776
  const fallbackCreatedAt = defaults.createdAt ?? now;
10408
10777
  const fallbackUpdatedAt = defaults.updatedAt ?? fallbackCreatedAt;
10409
- const title = typeof parsed.data.title === "string" ? parsed.data.title : path18.basename(relativePath, ".md");
10778
+ const title = typeof parsed.data.title === "string" ? parsed.data.title : path19.basename(relativePath, ".md");
10410
10779
  const kind = inferPageKind(relativePath, parsed.data.kind);
10411
10780
  const sourceIds = normalizeStringArray(parsed.data.source_ids);
10412
10781
  const projectIds = normalizeProjectIds(parsed.data.project_ids);
@@ -10447,18 +10816,18 @@ function parseStoredPage(relativePath, content, defaults = {}) {
10447
10816
  };
10448
10817
  }
10449
10818
  async function loadInsightPages(wikiDir) {
10450
- const insightsDir = path18.join(wikiDir, "insights");
10819
+ const insightsDir = path19.join(wikiDir, "insights");
10451
10820
  if (!await fileExists(insightsDir)) {
10452
10821
  return [];
10453
10822
  }
10454
- const files = (await listFilesRecursive(insightsDir)).filter((filePath) => filePath.endsWith(".md")).filter((filePath) => path18.basename(filePath) !== "index.md").sort((left, right) => left.localeCompare(right));
10823
+ const files = (await listFilesRecursive(insightsDir)).filter((filePath) => filePath.endsWith(".md")).filter((filePath) => path19.basename(filePath) !== "index.md").sort((left, right) => left.localeCompare(right));
10455
10824
  const insights = [];
10456
10825
  for (const absolutePath of files) {
10457
- const relativePath = toPosix(path18.relative(wikiDir, absolutePath));
10458
- const content = await fs14.readFile(absolutePath, "utf8");
10826
+ const relativePath = toPosix(path19.relative(wikiDir, absolutePath));
10827
+ const content = await fs15.readFile(absolutePath, "utf8");
10459
10828
  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");
10829
+ const stats = await fs15.stat(absolutePath);
10830
+ const title = typeof parsed.data.title === "string" ? parsed.data.title : path19.basename(absolutePath, ".md");
10462
10831
  const sourceIds = normalizeStringArray(parsed.data.source_ids);
10463
10832
  const projectIds = normalizeProjectIds(parsed.data.project_ids);
10464
10833
  const nodeIds = normalizeStringArray(parsed.data.node_ids);
@@ -10521,79 +10890,94 @@ function relatedOutputsForPage(targetPage, outputPages) {
10521
10890
  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
10891
  }
10523
10892
  async function resolveUniqueOutputSlug(wikiDir, baseSlug) {
10524
- const outputsDir = path19.join(wikiDir, "outputs");
10893
+ const outputsDir = path20.join(wikiDir, "outputs");
10525
10894
  const root = baseSlug || "output";
10526
10895
  let candidate = root;
10527
10896
  let counter = 2;
10528
- while (await fileExists(path19.join(outputsDir, `${candidate}.md`))) {
10897
+ while (await fileExists(path20.join(outputsDir, `${candidate}.md`))) {
10529
10898
  candidate = `${root}-${counter}`;
10530
10899
  counter++;
10531
10900
  }
10532
10901
  return candidate;
10533
10902
  }
10534
10903
  async function loadSavedOutputPages(wikiDir) {
10535
- const outputsDir = path19.join(wikiDir, "outputs");
10536
- const entries = await fs15.readdir(outputsDir, { withFileTypes: true }).catch(() => []);
10904
+ const outputsDir = path20.join(wikiDir, "outputs");
10905
+ const entries = await fs16.readdir(outputsDir, { withFileTypes: true }).catch(() => []);
10537
10906
  const outputs = [];
10538
- for (const entry of entries) {
10539
- if (!entry.isFile() || !entry.name.endsWith(".md") || entry.name === "index.md") {
10907
+ const queue = [{ absoluteDir: outputsDir, relativeDir: "outputs" }];
10908
+ while (queue.length > 0) {
10909
+ const current = queue.shift();
10910
+ if (!current) {
10540
10911
  continue;
10541
10912
  }
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
- });
10913
+ const currentEntries = current.absoluteDir === outputsDir ? entries : await fs16.readdir(current.absoluteDir, { withFileTypes: true }).catch(() => []);
10914
+ for (const entry of currentEntries) {
10915
+ if (entry.isDirectory()) {
10916
+ queue.push({
10917
+ absoluteDir: path20.join(current.absoluteDir, entry.name),
10918
+ relativeDir: path20.posix.join(current.relativeDir, entry.name)
10919
+ });
10920
+ continue;
10921
+ }
10922
+ if (!entry.isFile() || !entry.name.endsWith(".md") || entry.name === "index.md") {
10923
+ continue;
10924
+ }
10925
+ const relativePath = path20.posix.join(current.relativeDir, entry.name);
10926
+ const absolutePath = path20.join(current.absoluteDir, entry.name);
10927
+ const content = await fs16.readFile(absolutePath, "utf8");
10928
+ const parsed = matter7(content);
10929
+ const slug = relativePath.replace(/^outputs\//, "").replace(/\.md$/, "");
10930
+ const title = typeof parsed.data.title === "string" ? parsed.data.title : path20.basename(slug);
10931
+ const pageId = typeof parsed.data.page_id === "string" ? parsed.data.page_id : `output:${slug}`;
10932
+ const sourceIds = normalizeStringArray(parsed.data.source_ids);
10933
+ const projectIds = normalizeProjectIds(parsed.data.project_ids);
10934
+ const nodeIds = normalizeStringArray(parsed.data.node_ids);
10935
+ const relatedPageIds = normalizeStringArray(parsed.data.related_page_ids);
10936
+ const relatedNodeIds = normalizeStringArray(parsed.data.related_node_ids);
10937
+ const relatedSourceIds = normalizeStringArray(parsed.data.related_source_ids);
10938
+ const backlinks = normalizeStringArray(parsed.data.backlinks);
10939
+ const compiledFrom = normalizeStringArray(parsed.data.compiled_from);
10940
+ const stats = await fs16.stat(absolutePath);
10941
+ const createdAt = typeof parsed.data.created_at === "string" ? parsed.data.created_at : stats.birthtimeMs > 0 ? stats.birthtime.toISOString() : stats.mtime.toISOString();
10942
+ const updatedAt = typeof parsed.data.updated_at === "string" ? parsed.data.updated_at : stats.mtime.toISOString();
10943
+ outputs.push({
10944
+ page: {
10945
+ id: pageId,
10946
+ path: relativePath,
10947
+ title,
10948
+ kind: "output",
10949
+ sourceIds,
10950
+ projectIds,
10951
+ nodeIds,
10952
+ freshness: parsed.data.freshness === "stale" ? "stale" : "fresh",
10953
+ status: normalizePageStatus(parsed.data.status, "active"),
10954
+ confidence: typeof parsed.data.confidence === "number" ? parsed.data.confidence : 0.74,
10955
+ backlinks,
10956
+ schemaHash: typeof parsed.data.schema_hash === "string" ? parsed.data.schema_hash : "",
10957
+ sourceHashes: normalizeSourceHashes(parsed.data.source_hashes),
10958
+ relatedPageIds,
10959
+ relatedNodeIds,
10960
+ relatedSourceIds,
10961
+ createdAt,
10962
+ updatedAt,
10963
+ compiledFrom: compiledFrom.length ? compiledFrom : relatedSourceIds,
10964
+ managedBy: normalizePageManager(parsed.data.managed_by, "system"),
10965
+ origin: typeof parsed.data.origin === "string" ? parsed.data.origin : void 0,
10966
+ question: typeof parsed.data.question === "string" ? parsed.data.question : void 0,
10967
+ 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",
10968
+ outputAssets: normalizeOutputAssets(parsed.data.output_assets)
10969
+ },
10970
+ content,
10971
+ contentHash: sha256(content)
10972
+ });
10973
+ }
10590
10974
  }
10591
10975
  return outputs.sort((left, right) => left.page.title.localeCompare(right.page.title));
10592
10976
  }
10593
10977
 
10594
10978
  // src/search.ts
10595
- import fs16 from "fs/promises";
10596
- import path20 from "path";
10979
+ import fs17 from "fs/promises";
10980
+ import path21 from "path";
10597
10981
  import matter8 from "gray-matter";
10598
10982
  function getDatabaseSync() {
10599
10983
  const builtin = process.getBuiltinModule?.("node:sqlite");
@@ -10619,7 +11003,7 @@ function normalizeSourceClass2(value) {
10619
11003
  return value === "first_party" || value === "third_party" || value === "resource" || value === "generated" ? value : void 0;
10620
11004
  }
10621
11005
  async function rebuildSearchIndex(dbPath, pages, wikiDir) {
10622
- await ensureDir(path20.dirname(dbPath));
11006
+ await ensureDir(path21.dirname(dbPath));
10623
11007
  const DatabaseSync = getDatabaseSync();
10624
11008
  const db = new DatabaseSync(dbPath);
10625
11009
  db.exec("PRAGMA journal_mode = WAL;");
@@ -10651,8 +11035,8 @@ async function rebuildSearchIndex(dbPath, pages, wikiDir) {
10651
11035
  "INSERT INTO pages (id, path, title, body, kind, status, source_type, source_class, project_ids, project_key) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
10652
11036
  );
10653
11037
  for (const page of pages) {
10654
- const absolutePath = path20.join(wikiDir, page.path);
10655
- const content = await fs16.readFile(absolutePath, "utf8");
11038
+ const absolutePath = path21.join(wikiDir, page.path);
11039
+ const content = await fs17.readFile(absolutePath, "utf8");
10656
11040
  const parsed = matter8(content);
10657
11041
  insertPage.run(
10658
11042
  page.id,
@@ -10801,7 +11185,7 @@ function outputFormatInstruction(format) {
10801
11185
  }
10802
11186
  }
10803
11187
  function outputAssetPath(slug, fileName) {
10804
- return toPosix(path21.join("outputs", "assets", slug, fileName));
11188
+ return toPosix(path22.join("outputs", "assets", slug, fileName));
10805
11189
  }
10806
11190
  function outputAssetId(slug, role) {
10807
11191
  return `output:${slug}:asset:${role}`;
@@ -10941,7 +11325,7 @@ async function resolveImageGenerationProvider(rootDir) {
10941
11325
  if (!providerConfig) {
10942
11326
  throw new Error(`No provider configured with id "${preferredProviderId}" for task "imageProvider".`);
10943
11327
  }
10944
- const { createProvider: createProvider2 } = await import("./registry-G7NSRYCO.js");
11328
+ const { createProvider: createProvider2 } = await import("./registry-2REAPKPO.js");
10945
11329
  return createProvider2(preferredProviderId, providerConfig, rootDir);
10946
11330
  }
10947
11331
  async function generateOutputArtifacts(rootDir, input) {
@@ -11139,7 +11523,7 @@ async function generateOutputArtifacts(rootDir, input) {
11139
11523
  };
11140
11524
  }
11141
11525
  function normalizeProjectRoot(root) {
11142
- const normalized = toPosix(path21.posix.normalize(root.replace(/\\/g, "/"))).replace(/^\.\/+/, "").replace(/\/+$/, "");
11526
+ const normalized = toPosix(path22.posix.normalize(root.replace(/\\/g, "/"))).replace(/^\.\/+/, "").replace(/\/+$/, "");
11143
11527
  return normalized;
11144
11528
  }
11145
11529
  function projectEntries(config) {
@@ -11165,10 +11549,10 @@ function manifestPathForProject(rootDir, manifest) {
11165
11549
  if (!rawPath) {
11166
11550
  return toPosix(manifest.storedPath);
11167
11551
  }
11168
- if (!path21.isAbsolute(rawPath)) {
11552
+ if (!path22.isAbsolute(rawPath)) {
11169
11553
  return normalizeProjectRoot(rawPath);
11170
11554
  }
11171
- const relative = toPosix(path21.relative(rootDir, rawPath));
11555
+ const relative = toPosix(path22.relative(rootDir, rawPath));
11172
11556
  return relative.startsWith("..") ? toPosix(rawPath) : normalizeProjectRoot(relative);
11173
11557
  }
11174
11558
  function prefixMatches(value, prefix) {
@@ -11342,7 +11726,7 @@ function pageHashes(pages) {
11342
11726
  return Object.fromEntries(pages.map((page) => [page.page.id, page.contentHash]));
11343
11727
  }
11344
11728
  async function buildManagedGraphPage(absolutePath, defaults, build) {
11345
- const existingContent = await fileExists(absolutePath) ? await fs17.readFile(absolutePath, "utf8") : null;
11729
+ const existingContent = await fileExists(absolutePath) ? await fs18.readFile(absolutePath, "utf8") : null;
11346
11730
  let existing = await loadExistingManagedPageState(absolutePath, {
11347
11731
  status: defaults.status ?? "active",
11348
11732
  managedBy: defaults.managedBy
@@ -11380,7 +11764,7 @@ async function buildManagedGraphPage(absolutePath, defaults, build) {
11380
11764
  return built;
11381
11765
  }
11382
11766
  async function buildManagedContent(absolutePath, defaults, build) {
11383
- const existingContent = await fileExists(absolutePath) ? await fs17.readFile(absolutePath, "utf8") : null;
11767
+ const existingContent = await fileExists(absolutePath) ? await fs18.readFile(absolutePath, "utf8") : null;
11384
11768
  let existing = await loadExistingManagedPageState(absolutePath, {
11385
11769
  status: defaults.status ?? "active",
11386
11770
  managedBy: defaults.managedBy
@@ -11503,7 +11887,7 @@ function resetGraphNodeMetrics(nodes) {
11503
11887
  return nodes.map(({ communityId: _communityId, degree: _degree, bridgeScore: _bridgeScore, isGodNode: _isGodNode, ...node }) => node);
11504
11888
  }
11505
11889
  function manifestRepoPath(manifest) {
11506
- return toPosix(manifest.repoRelativePath ?? path21.basename(manifest.originalPath ?? manifest.storedPath));
11890
+ return toPosix(manifest.repoRelativePath ?? path22.basename(manifest.originalPath ?? manifest.storedPath));
11507
11891
  }
11508
11892
  function goPackageScopeKey(manifest, analysis) {
11509
11893
  if (analysis.code?.language !== "go") {
@@ -11513,7 +11897,7 @@ function goPackageScopeKey(manifest, analysis) {
11513
11897
  if (!packageName) {
11514
11898
  return null;
11515
11899
  }
11516
- return `${packageName}:${path21.posix.dirname(manifestRepoPath(manifest))}`;
11900
+ return `${packageName}:${path22.posix.dirname(manifestRepoPath(manifest))}`;
11517
11901
  }
11518
11902
  function buildGoPackageSymbolLookups(analyses, manifestsById) {
11519
11903
  const lookups = /* @__PURE__ */ new Map();
@@ -11982,7 +12366,7 @@ async function buildGraphOrientationPages(graph, paths, schemaHash, previousComp
11982
12366
  const benchmark = await readJsonFile(paths.benchmarkPath);
11983
12367
  const communityRecords = [];
11984
12368
  for (const community of graph.communities ?? []) {
11985
- const absolutePath = path21.join(paths.wikiDir, "graph", "communities", `${community.id.replace(/^community:/, "")}.md`);
12369
+ const absolutePath = path22.join(paths.wikiDir, "graph", "communities", `${community.id.replace(/^community:/, "")}.md`);
11986
12370
  communityRecords.push(
11987
12371
  await buildManagedGraphPage(
11988
12372
  absolutePath,
@@ -12011,7 +12395,7 @@ async function buildGraphOrientationPages(graph, paths, schemaHash, previousComp
12011
12395
  graphHash: graphHash(graph),
12012
12396
  contradictions
12013
12397
  });
12014
- const reportAbsolutePath = path21.join(paths.wikiDir, "graph", "report.md");
12398
+ const reportAbsolutePath = path22.join(paths.wikiDir, "graph", "report.md");
12015
12399
  const reportRecord = await buildManagedGraphPage(
12016
12400
  reportAbsolutePath,
12017
12401
  {
@@ -12032,7 +12416,7 @@ async function buildGraphOrientationPages(graph, paths, schemaHash, previousComp
12032
12416
  };
12033
12417
  }
12034
12418
  async function writePage(wikiDir, relativePath, content, changedPages) {
12035
- const absolutePath = path21.resolve(wikiDir, relativePath);
12419
+ const absolutePath = path22.resolve(wikiDir, relativePath);
12036
12420
  const changed = await writeFileIfChanged(absolutePath, content);
12037
12421
  if (changed) {
12038
12422
  changedPages.push(relativePath);
@@ -12095,29 +12479,29 @@ async function requiredCompileArtifactsExist(paths) {
12095
12479
  paths.graphPath,
12096
12480
  paths.codeIndexPath,
12097
12481
  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")
12482
+ path22.join(paths.wikiDir, "index.md"),
12483
+ path22.join(paths.wikiDir, "sources", "index.md"),
12484
+ path22.join(paths.wikiDir, "code", "index.md"),
12485
+ path22.join(paths.wikiDir, "concepts", "index.md"),
12486
+ path22.join(paths.wikiDir, "entities", "index.md"),
12487
+ path22.join(paths.wikiDir, "outputs", "index.md"),
12488
+ path22.join(paths.wikiDir, "projects", "index.md"),
12489
+ path22.join(paths.wikiDir, "candidates", "index.md")
12106
12490
  ];
12107
12491
  const checks = await Promise.all(requiredPaths.map((filePath) => fileExists(filePath)));
12108
12492
  return checks.every(Boolean);
12109
12493
  }
12110
12494
  async function loadAvailableCachedAnalyses(paths, manifests) {
12111
12495
  const analyses = await Promise.all(
12112
- manifests.map(async (manifest) => readJsonFile(path21.join(paths.analysesDir, `${manifest.sourceId}.json`)))
12496
+ manifests.map(async (manifest) => readJsonFile(path22.join(paths.analysesDir, `${manifest.sourceId}.json`)))
12113
12497
  );
12114
12498
  return analyses.filter((analysis) => Boolean(analysis));
12115
12499
  }
12116
12500
  function approvalManifestPath(paths, approvalId) {
12117
- return path21.join(paths.approvalsDir, approvalId, "manifest.json");
12501
+ return path22.join(paths.approvalsDir, approvalId, "manifest.json");
12118
12502
  }
12119
12503
  function approvalGraphPath(paths, approvalId) {
12120
- return path21.join(paths.approvalsDir, approvalId, "state", "graph.json");
12504
+ return path22.join(paths.approvalsDir, approvalId, "state", "graph.json");
12121
12505
  }
12122
12506
  async function readApprovalManifest(paths, approvalId) {
12123
12507
  const manifest = await readJsonFile(approvalManifestPath(paths, approvalId));
@@ -12127,7 +12511,7 @@ async function readApprovalManifest(paths, approvalId) {
12127
12511
  return manifest;
12128
12512
  }
12129
12513
  async function writeApprovalManifest(paths, manifest) {
12130
- await fs17.writeFile(approvalManifestPath(paths, manifest.approvalId), `${JSON.stringify(manifest, null, 2)}
12514
+ await fs18.writeFile(approvalManifestPath(paths, manifest.approvalId), `${JSON.stringify(manifest, null, 2)}
12131
12515
  `, "utf8");
12132
12516
  }
12133
12517
  async function buildApprovalEntries(paths, changedFiles, deletedPaths, previousGraph, graph) {
@@ -12142,7 +12526,7 @@ async function buildApprovalEntries(paths, changedFiles, deletedPaths, previousG
12142
12526
  continue;
12143
12527
  }
12144
12528
  const previousPage = previousPagesById.get(nextPage.id);
12145
- const currentExists = await fileExists(path21.join(paths.wikiDir, file.relativePath));
12529
+ const currentExists = await fileExists(path22.join(paths.wikiDir, file.relativePath));
12146
12530
  if (previousPage && previousPage.path !== nextPage.path) {
12147
12531
  entries.push({
12148
12532
  pageId: nextPage.id,
@@ -12175,7 +12559,7 @@ async function buildApprovalEntries(paths, changedFiles, deletedPaths, previousG
12175
12559
  const previousPage = previousPagesByPath.get(deletedPath);
12176
12560
  entries.push({
12177
12561
  pageId: previousPage?.id ?? `page:${slugify(deletedPath)}`,
12178
- title: previousPage?.title ?? path21.basename(deletedPath, ".md"),
12562
+ title: previousPage?.title ?? path22.basename(deletedPath, ".md"),
12179
12563
  kind: previousPage?.kind ?? "index",
12180
12564
  changeType: "delete",
12181
12565
  status: "pending",
@@ -12187,16 +12571,16 @@ async function buildApprovalEntries(paths, changedFiles, deletedPaths, previousG
12187
12571
  }
12188
12572
  async function stageApprovalBundle(paths, changedFiles, deletedPaths, previousGraph, graph) {
12189
12573
  const approvalId = `compile-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}`;
12190
- const approvalDir = path21.join(paths.approvalsDir, approvalId);
12574
+ const approvalDir = path22.join(paths.approvalsDir, approvalId);
12191
12575
  await ensureDir(approvalDir);
12192
- await ensureDir(path21.join(approvalDir, "wiki"));
12193
- await ensureDir(path21.join(approvalDir, "state"));
12576
+ await ensureDir(path22.join(approvalDir, "wiki"));
12577
+ await ensureDir(path22.join(approvalDir, "state"));
12194
12578
  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");
12579
+ const targetPath = path22.join(approvalDir, "wiki", file.relativePath);
12580
+ await ensureDir(path22.dirname(targetPath));
12581
+ await fs18.writeFile(targetPath, file.content, "utf8");
12198
12582
  }
12199
- await fs17.writeFile(path21.join(approvalDir, "state", "graph.json"), JSON.stringify(graph, null, 2), "utf8");
12583
+ await fs18.writeFile(path22.join(approvalDir, "state", "graph.json"), JSON.stringify(graph, null, 2), "utf8");
12200
12584
  await writeApprovalManifest(paths, {
12201
12585
  approvalId,
12202
12586
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -12258,7 +12642,7 @@ async function syncVaultArtifacts(rootDir, input) {
12258
12642
  confidence: 1
12259
12643
  });
12260
12644
  const sourceRecord = await buildManagedGraphPage(
12261
- path21.join(paths.wikiDir, preview.path),
12645
+ path22.join(paths.wikiDir, preview.path),
12262
12646
  {
12263
12647
  managedBy: "system",
12264
12648
  confidence: 1,
@@ -12304,7 +12688,7 @@ async function syncVaultArtifacts(rootDir, input) {
12304
12688
  );
12305
12689
  records.push(
12306
12690
  await buildManagedGraphPage(
12307
- path21.join(paths.wikiDir, modulePreview.path),
12691
+ path22.join(paths.wikiDir, modulePreview.path),
12308
12692
  {
12309
12693
  managedBy: "system",
12310
12694
  confidence: 1,
@@ -12338,8 +12722,8 @@ async function syncVaultArtifacts(rootDir, input) {
12338
12722
  const relativePath = promoted ? activeAggregatePath(itemKind, slug) : candidatePagePathFor(itemKind, slug);
12339
12723
  const aggregateSourceClass2 = aggregateManifestSourceClass(input.manifests, sourceIds);
12340
12724
  const fallbackPaths = [
12341
- path21.join(paths.wikiDir, activeAggregatePath(itemKind, slug)),
12342
- path21.join(paths.wikiDir, candidatePagePathFor(itemKind, slug))
12725
+ path22.join(paths.wikiDir, activeAggregatePath(itemKind, slug)),
12726
+ path22.join(paths.wikiDir, candidatePagePathFor(itemKind, slug))
12343
12727
  ];
12344
12728
  const confidence = nodeConfidence(aggregate.sourceAnalyses.length);
12345
12729
  const preview = emptyGraphPage({
@@ -12357,7 +12741,7 @@ async function syncVaultArtifacts(rootDir, input) {
12357
12741
  status: promoted ? "active" : "candidate"
12358
12742
  });
12359
12743
  const pageRecord = await buildManagedGraphPage(
12360
- path21.join(paths.wikiDir, relativePath),
12744
+ path22.join(paths.wikiDir, relativePath),
12361
12745
  {
12362
12746
  status: promoted ? "active" : "candidate",
12363
12747
  managedBy: "system",
@@ -12474,7 +12858,7 @@ async function syncVaultArtifacts(rootDir, input) {
12474
12858
  confidence: 1
12475
12859
  }),
12476
12860
  content: await buildManagedContent(
12477
- path21.join(paths.wikiDir, "projects", "index.md"),
12861
+ path22.join(paths.wikiDir, "projects", "index.md"),
12478
12862
  {
12479
12863
  managedBy: "system",
12480
12864
  compiledFrom: indexCompiledFrom(projectIndexRefs)
@@ -12498,7 +12882,7 @@ async function syncVaultArtifacts(rootDir, input) {
12498
12882
  records.push({
12499
12883
  page: projectIndexRef,
12500
12884
  content: await buildManagedContent(
12501
- path21.join(paths.wikiDir, projectIndexRef.path),
12885
+ path22.join(paths.wikiDir, projectIndexRef.path),
12502
12886
  {
12503
12887
  managedBy: "system",
12504
12888
  compiledFrom: indexCompiledFrom(Object.values(sections).flat())
@@ -12526,7 +12910,7 @@ async function syncVaultArtifacts(rootDir, input) {
12526
12910
  confidence: 1
12527
12911
  }),
12528
12912
  content: await buildManagedContent(
12529
- path21.join(paths.wikiDir, "index.md"),
12913
+ path22.join(paths.wikiDir, "index.md"),
12530
12914
  {
12531
12915
  managedBy: "system",
12532
12916
  compiledFrom: indexCompiledFrom(allPages)
@@ -12557,7 +12941,7 @@ async function syncVaultArtifacts(rootDir, input) {
12557
12941
  confidence: 1
12558
12942
  }),
12559
12943
  content: await buildManagedContent(
12560
- path21.join(paths.wikiDir, relativePath),
12944
+ path22.join(paths.wikiDir, relativePath),
12561
12945
  {
12562
12946
  managedBy: "system",
12563
12947
  compiledFrom: indexCompiledFrom(pages)
@@ -12568,12 +12952,12 @@ async function syncVaultArtifacts(rootDir, input) {
12568
12952
  }
12569
12953
  const nextPagePaths = new Set(records.map((record) => record.page.path));
12570
12954
  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));
12955
+ 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
12956
  const obsoletePaths = uniqueStrings3([...obsoleteGraphPaths, ...existingProjectIndexPaths]);
12573
12957
  const changedFiles = [];
12574
12958
  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;
12959
+ const absolutePath = path22.join(paths.wikiDir, record.page.path);
12960
+ const current = await fileExists(absolutePath) ? await fs18.readFile(absolutePath, "utf8") : null;
12577
12961
  if (current !== record.content) {
12578
12962
  changedPages.push(record.page.path);
12579
12963
  changedFiles.push({ relativePath: record.page.path, content: record.content });
@@ -12598,10 +12982,10 @@ async function syncVaultArtifacts(rootDir, input) {
12598
12982
  await writePage(paths.wikiDir, record.page.path, record.content, writeChanges);
12599
12983
  }
12600
12984
  for (const relativePath of obsoletePaths) {
12601
- await fs17.rm(path21.join(paths.wikiDir, relativePath), { force: true });
12985
+ await fs18.rm(path22.join(paths.wikiDir, relativePath), { force: true });
12602
12986
  }
12603
12987
  await writeJsonFile(paths.graphPath, graph);
12604
- await writeJsonFile(path21.join(paths.wikiDir, "graph", "report.json"), graphOrientation.report);
12988
+ await writeJsonFile(path22.join(paths.wikiDir, "graph", "report.json"), graphOrientation.report);
12605
12989
  await writeJsonFile(paths.codeIndexPath, input.codeIndex);
12606
12990
  await writeJsonFile(paths.compileStatePath, {
12607
12991
  generatedAt: graph.generatedAt,
@@ -12672,17 +13056,17 @@ async function refreshIndexesAndSearch(rootDir, pages) {
12672
13056
  })
12673
13057
  );
12674
13058
  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"))
13059
+ ensureDir(path22.join(paths.wikiDir, "sources")),
13060
+ ensureDir(path22.join(paths.wikiDir, "code")),
13061
+ ensureDir(path22.join(paths.wikiDir, "concepts")),
13062
+ ensureDir(path22.join(paths.wikiDir, "entities")),
13063
+ ensureDir(path22.join(paths.wikiDir, "outputs")),
13064
+ ensureDir(path22.join(paths.wikiDir, "graph")),
13065
+ ensureDir(path22.join(paths.wikiDir, "graph", "communities")),
13066
+ ensureDir(path22.join(paths.wikiDir, "projects")),
13067
+ ensureDir(path22.join(paths.wikiDir, "candidates"))
12684
13068
  ]);
12685
- const projectsIndexPath = path21.join(paths.wikiDir, "projects", "index.md");
13069
+ const projectsIndexPath = path22.join(paths.wikiDir, "projects", "index.md");
12686
13070
  await writeFileIfChanged(
12687
13071
  projectsIndexPath,
12688
13072
  await buildManagedContent(
@@ -12703,7 +13087,7 @@ async function refreshIndexesAndSearch(rootDir, pages) {
12703
13087
  outputs: pages.filter((page) => page.kind === "output" && page.projectIds.includes(project.id)),
12704
13088
  candidates: pages.filter((page) => page.status === "candidate" && page.projectIds.includes(project.id))
12705
13089
  };
12706
- const absolutePath = path21.join(paths.wikiDir, "projects", project.id, "index.md");
13090
+ const absolutePath = path22.join(paths.wikiDir, "projects", project.id, "index.md");
12707
13091
  await writeFileIfChanged(
12708
13092
  absolutePath,
12709
13093
  await buildManagedContent(
@@ -12721,7 +13105,7 @@ async function refreshIndexesAndSearch(rootDir, pages) {
12721
13105
  )
12722
13106
  );
12723
13107
  }
12724
- const rootIndexPath = path21.join(paths.wikiDir, "index.md");
13108
+ const rootIndexPath = path22.join(paths.wikiDir, "index.md");
12725
13109
  await writeFileIfChanged(
12726
13110
  rootIndexPath,
12727
13111
  await buildManagedContent(
@@ -12742,7 +13126,7 @@ async function refreshIndexesAndSearch(rootDir, pages) {
12742
13126
  ["candidates/index.md", "candidates", pagesWithGraph.filter((page) => page.status === "candidate")],
12743
13127
  ["graph/index.md", "graph", pagesWithGraph.filter((page) => page.kind === "graph_report" || page.kind === "community_summary")]
12744
13128
  ]) {
12745
- const absolutePath = path21.join(paths.wikiDir, relativePath);
13129
+ const absolutePath = path22.join(paths.wikiDir, relativePath);
12746
13130
  await writeFileIfChanged(
12747
13131
  absolutePath,
12748
13132
  await buildManagedContent(
@@ -12756,23 +13140,23 @@ async function refreshIndexesAndSearch(rootDir, pages) {
12756
13140
  );
12757
13141
  }
12758
13142
  for (const record of graphOrientation.records) {
12759
- await writeFileIfChanged(path21.join(paths.wikiDir, record.page.path), record.content);
13143
+ await writeFileIfChanged(path22.join(paths.wikiDir, record.page.path), record.content);
12760
13144
  }
12761
13145
  if (graphOrientation.report) {
12762
- await writeJsonFile(path21.join(paths.wikiDir, "graph", "report.json"), graphOrientation.report);
13146
+ await writeJsonFile(path22.join(paths.wikiDir, "graph", "report.json"), graphOrientation.report);
12763
13147
  }
12764
- const existingProjectIndexPaths = (await listFilesRecursive(paths.projectsDir)).filter((absolutePath) => absolutePath.endsWith(".md")).map((absolutePath) => toPosix(path21.relative(paths.wikiDir, absolutePath)));
13148
+ const existingProjectIndexPaths = (await listFilesRecursive(paths.projectsDir)).filter((absolutePath) => absolutePath.endsWith(".md")).map((absolutePath) => toPosix(path22.relative(paths.wikiDir, absolutePath)));
12765
13149
  const allowedProjectIndexPaths = /* @__PURE__ */ new Set([
12766
13150
  "projects/index.md",
12767
13151
  ...configuredProjects.map((project) => `projects/${project.id}/index.md`)
12768
13152
  ]);
12769
13153
  await Promise.all(
12770
- existingProjectIndexPaths.filter((relativePath) => !allowedProjectIndexPaths.has(relativePath)).map((relativePath) => fs17.rm(path21.join(paths.wikiDir, relativePath), { force: true }))
13154
+ existingProjectIndexPaths.filter((relativePath) => !allowedProjectIndexPaths.has(relativePath)).map((relativePath) => fs18.rm(path22.join(paths.wikiDir, relativePath), { force: true }))
12771
13155
  );
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)));
13156
+ 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
13157
  const allowedGraphPages = /* @__PURE__ */ new Set(["graph/index.md", ...graphOrientation.records.map((record) => record.page.path)]);
12774
13158
  await Promise.all(
12775
- existingGraphPages.filter((relativePath) => !allowedGraphPages.has(relativePath)).map((relativePath) => fs17.rm(path21.join(paths.wikiDir, relativePath), { force: true }))
13159
+ existingGraphPages.filter((relativePath) => !allowedGraphPages.has(relativePath)).map((relativePath) => fs18.rm(path22.join(paths.wikiDir, relativePath), { force: true }))
12776
13160
  );
12777
13161
  await rebuildSearchIndex(paths.searchDbPath, pagesWithGraph, paths.wikiDir);
12778
13162
  }
@@ -12792,7 +13176,7 @@ async function prepareOutputPageSave(rootDir, input) {
12792
13176
  confidence: 0.74
12793
13177
  }
12794
13178
  });
12795
- const absolutePath = path21.join(paths.wikiDir, output.page.path);
13179
+ const absolutePath = path22.join(paths.wikiDir, output.page.path);
12796
13180
  return {
12797
13181
  page: output.page,
12798
13182
  savedPath: absolutePath,
@@ -12804,15 +13188,15 @@ async function prepareOutputPageSave(rootDir, input) {
12804
13188
  async function persistOutputPage(rootDir, input) {
12805
13189
  const { paths } = await loadVaultConfig(rootDir);
12806
13190
  const prepared = await prepareOutputPageSave(rootDir, input);
12807
- await ensureDir(path21.dirname(prepared.savedPath));
12808
- await fs17.writeFile(prepared.savedPath, prepared.content, "utf8");
13191
+ await ensureDir(path22.dirname(prepared.savedPath));
13192
+ await fs18.writeFile(prepared.savedPath, prepared.content, "utf8");
12809
13193
  for (const assetFile of prepared.assetFiles) {
12810
- const assetPath = path21.join(paths.wikiDir, assetFile.relativePath);
12811
- await ensureDir(path21.dirname(assetPath));
13194
+ const assetPath = path22.join(paths.wikiDir, assetFile.relativePath);
13195
+ await ensureDir(path22.dirname(assetPath));
12812
13196
  if (typeof assetFile.content === "string") {
12813
- await fs17.writeFile(assetPath, assetFile.content, assetFile.encoding ?? "utf8");
13197
+ await fs18.writeFile(assetPath, assetFile.content, assetFile.encoding ?? "utf8");
12814
13198
  } else {
12815
- await fs17.writeFile(assetPath, assetFile.content);
13199
+ await fs18.writeFile(assetPath, assetFile.content);
12816
13200
  }
12817
13201
  }
12818
13202
  return { page: prepared.page, savedPath: prepared.savedPath, outputAssets: prepared.outputAssets };
@@ -12833,7 +13217,7 @@ async function prepareExploreHubSave(rootDir, input) {
12833
13217
  confidence: 0.76
12834
13218
  }
12835
13219
  });
12836
- const absolutePath = path21.join(paths.wikiDir, hub.page.path);
13220
+ const absolutePath = path22.join(paths.wikiDir, hub.page.path);
12837
13221
  return {
12838
13222
  page: hub.page,
12839
13223
  savedPath: absolutePath,
@@ -12845,15 +13229,15 @@ async function prepareExploreHubSave(rootDir, input) {
12845
13229
  async function persistExploreHub(rootDir, input) {
12846
13230
  const { paths } = await loadVaultConfig(rootDir);
12847
13231
  const prepared = await prepareExploreHubSave(rootDir, input);
12848
- await ensureDir(path21.dirname(prepared.savedPath));
12849
- await fs17.writeFile(prepared.savedPath, prepared.content, "utf8");
13232
+ await ensureDir(path22.dirname(prepared.savedPath));
13233
+ await fs18.writeFile(prepared.savedPath, prepared.content, "utf8");
12850
13234
  for (const assetFile of prepared.assetFiles) {
12851
- const assetPath = path21.join(paths.wikiDir, assetFile.relativePath);
12852
- await ensureDir(path21.dirname(assetPath));
13235
+ const assetPath = path22.join(paths.wikiDir, assetFile.relativePath);
13236
+ await ensureDir(path22.dirname(assetPath));
12853
13237
  if (typeof assetFile.content === "string") {
12854
- await fs17.writeFile(assetPath, assetFile.content, assetFile.encoding ?? "utf8");
13238
+ await fs18.writeFile(assetPath, assetFile.content, assetFile.encoding ?? "utf8");
12855
13239
  } else {
12856
- await fs17.writeFile(assetPath, assetFile.content);
13240
+ await fs18.writeFile(assetPath, assetFile.content);
12857
13241
  }
12858
13242
  }
12859
13243
  return { page: prepared.page, savedPath: prepared.savedPath, outputAssets: prepared.outputAssets };
@@ -12870,17 +13254,17 @@ async function stageOutputApprovalBundle(rootDir, stagedPages) {
12870
13254
  }))
12871
13255
  ]);
12872
13256
  const approvalId = `schedule-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}`;
12873
- const approvalDir = path21.join(paths.approvalsDir, approvalId);
13257
+ const approvalDir = path22.join(paths.approvalsDir, approvalId);
12874
13258
  await ensureDir(approvalDir);
12875
- await ensureDir(path21.join(approvalDir, "wiki"));
12876
- await ensureDir(path21.join(approvalDir, "state"));
13259
+ await ensureDir(path22.join(approvalDir, "wiki"));
13260
+ await ensureDir(path22.join(approvalDir, "state"));
12877
13261
  for (const file of changedFiles) {
12878
- const targetPath = path21.join(approvalDir, "wiki", file.relativePath);
12879
- await ensureDir(path21.dirname(targetPath));
13262
+ const targetPath = path22.join(approvalDir, "wiki", file.relativePath);
13263
+ await ensureDir(path22.dirname(targetPath));
12880
13264
  if ("binary" in file && file.binary) {
12881
- await fs17.writeFile(targetPath, Buffer.from(file.content, "base64"));
13265
+ await fs18.writeFile(targetPath, Buffer.from(file.content, "base64"));
12882
13266
  } else {
12883
- await fs17.writeFile(targetPath, file.content, "utf8");
13267
+ await fs18.writeFile(targetPath, file.content, "utf8");
12884
13268
  }
12885
13269
  }
12886
13270
  const nextPages = sortGraphPages([
@@ -12895,7 +13279,7 @@ async function stageOutputApprovalBundle(rootDir, stagedPages) {
12895
13279
  sources: previousGraph?.sources ?? [],
12896
13280
  pages: nextPages
12897
13281
  };
12898
- await fs17.writeFile(path21.join(approvalDir, "state", "graph.json"), JSON.stringify(graph, null, 2), "utf8");
13282
+ await fs18.writeFile(path22.join(approvalDir, "state", "graph.json"), JSON.stringify(graph, null, 2), "utf8");
12899
13283
  await writeApprovalManifest(paths, {
12900
13284
  approvalId,
12901
13285
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -12924,9 +13308,9 @@ async function executeQuery(rootDir, question, format) {
12924
13308
  const searchResults = searchPages(paths.searchDbPath, question, 5);
12925
13309
  const excerpts = await Promise.all(
12926
13310
  searchResults.map(async (result) => {
12927
- const absolutePath = path21.join(paths.wikiDir, result.path);
13311
+ const absolutePath = path22.join(paths.wikiDir, result.path);
12928
13312
  try {
12929
- const content = await fs17.readFile(absolutePath, "utf8");
13313
+ const content = await fs18.readFile(absolutePath, "utf8");
12930
13314
  const parsed = matter9(content);
12931
13315
  return `# ${result.title}
12932
13316
  ${truncate(normalizeWhitespace(parsed.content), 1200)}`;
@@ -13160,7 +13544,7 @@ function computeChangeSummary(current, staged, changeType) {
13160
13544
  async function listApprovals(rootDir) {
13161
13545
  const { paths } = await loadVaultConfig(rootDir);
13162
13546
  const manifests = await Promise.all(
13163
- (await fs17.readdir(paths.approvalsDir, { withFileTypes: true }).catch(() => [])).filter((entry) => entry.isDirectory()).map(async (entry) => {
13547
+ (await fs18.readdir(paths.approvalsDir, { withFileTypes: true }).catch(() => [])).filter((entry) => entry.isDirectory()).map(async (entry) => {
13164
13548
  try {
13165
13549
  return await readApprovalManifest(paths, entry.name);
13166
13550
  } catch {
@@ -13176,8 +13560,8 @@ async function readApproval(rootDir, approvalId, options) {
13176
13560
  const details = await Promise.all(
13177
13561
  manifest.entries.map(async (entry) => {
13178
13562
  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;
13563
+ const currentContent = currentPath ? await fs18.readFile(path22.join(paths.wikiDir, currentPath), "utf8").catch(() => void 0) : void 0;
13564
+ const stagedContent = entry.nextPath ? await fs18.readFile(path22.join(paths.approvalsDir, approvalId, "wiki", entry.nextPath), "utf8").catch(() => void 0) : void 0;
13181
13565
  const detail = {
13182
13566
  ...entry,
13183
13567
  currentContent,
@@ -13210,26 +13594,26 @@ async function acceptApproval(rootDir, approvalId, targets = []) {
13210
13594
  if (!entry.nextPath) {
13211
13595
  throw new Error(`Approval entry ${entry.pageId} is missing a staged path.`);
13212
13596
  }
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");
13597
+ const stagedAbsolutePath = path22.join(paths.approvalsDir, approvalId, "wiki", entry.nextPath);
13598
+ const stagedContent = await fs18.readFile(stagedAbsolutePath, "utf8");
13599
+ const targetAbsolutePath = path22.join(paths.wikiDir, entry.nextPath);
13600
+ await ensureDir(path22.dirname(targetAbsolutePath));
13601
+ await fs18.writeFile(targetAbsolutePath, stagedContent, "utf8");
13218
13602
  if (entry.changeType === "promote" && entry.previousPath) {
13219
- await fs17.rm(path21.join(paths.wikiDir, entry.previousPath), { force: true });
13603
+ await fs18.rm(path22.join(paths.wikiDir, entry.previousPath), { force: true });
13220
13604
  }
13221
13605
  const nextPage = bundleGraph?.pages.find((page) => page.id === entry.pageId && page.path === entry.nextPath) ?? parseStoredPage(entry.nextPath, stagedContent);
13222
13606
  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 });
13607
+ const outputAssetDir = path22.join(paths.wikiDir, "outputs", "assets", path22.basename(nextPage.path, ".md"));
13608
+ await fs18.rm(outputAssetDir, { recursive: true, force: true });
13225
13609
  for (const asset of nextPage.outputAssets) {
13226
- const stagedAssetPath = path21.join(paths.approvalsDir, approvalId, "wiki", asset.path);
13610
+ const stagedAssetPath = path22.join(paths.approvalsDir, approvalId, "wiki", asset.path);
13227
13611
  if (!await fileExists(stagedAssetPath)) {
13228
13612
  continue;
13229
13613
  }
13230
- const targetAssetPath = path21.join(paths.wikiDir, asset.path);
13231
- await ensureDir(path21.dirname(targetAssetPath));
13232
- await fs17.copyFile(stagedAssetPath, targetAssetPath);
13614
+ const targetAssetPath = path22.join(paths.wikiDir, asset.path);
13615
+ await ensureDir(path22.dirname(targetAssetPath));
13616
+ await fs18.copyFile(stagedAssetPath, targetAssetPath);
13233
13617
  }
13234
13618
  }
13235
13619
  nextPages = nextPages.filter(
@@ -13240,10 +13624,10 @@ async function acceptApproval(rootDir, approvalId, targets = []) {
13240
13624
  } else {
13241
13625
  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
13626
  if (entry.previousPath) {
13243
- await fs17.rm(path21.join(paths.wikiDir, entry.previousPath), { force: true });
13627
+ await fs18.rm(path22.join(paths.wikiDir, entry.previousPath), { force: true });
13244
13628
  }
13245
13629
  if (deletedPage?.kind === "output") {
13246
- await fs17.rm(path21.join(paths.wikiDir, "outputs", "assets", path21.basename(deletedPage.path, ".md")), {
13630
+ await fs18.rm(path22.join(paths.wikiDir, "outputs", "assets", path22.basename(deletedPage.path, ".md")), {
13247
13631
  recursive: true,
13248
13632
  force: true
13249
13633
  });
@@ -13334,7 +13718,7 @@ async function promoteCandidate(rootDir, target) {
13334
13718
  const { paths } = await loadVaultConfig(rootDir);
13335
13719
  const graph = await readJsonFile(paths.graphPath);
13336
13720
  const candidate = resolveCandidateTarget(graph?.pages ?? [], target);
13337
- const raw = await fs17.readFile(path21.join(paths.wikiDir, candidate.path), "utf8");
13721
+ const raw = await fs18.readFile(path22.join(paths.wikiDir, candidate.path), "utf8");
13338
13722
  const parsed = matter9(raw);
13339
13723
  const nextUpdatedAt = (/* @__PURE__ */ new Date()).toISOString();
13340
13724
  const nextContent = matter9.stringify(parsed.content, {
@@ -13346,10 +13730,10 @@ async function promoteCandidate(rootDir, target) {
13346
13730
  )
13347
13731
  });
13348
13732
  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 });
13733
+ const nextAbsolutePath = path22.join(paths.wikiDir, nextPath);
13734
+ await ensureDir(path22.dirname(nextAbsolutePath));
13735
+ await fs18.writeFile(nextAbsolutePath, nextContent, "utf8");
13736
+ await fs18.rm(path22.join(paths.wikiDir, candidate.path), { force: true });
13353
13737
  const nextPage = parseStoredPage(nextPath, nextContent, { createdAt: candidate.createdAt, updatedAt: nextUpdatedAt });
13354
13738
  const nextPages = sortGraphPages(
13355
13739
  (graph?.pages ?? []).filter((page) => page.id !== candidate.id && page.path !== candidate.path).concat(nextPage)
@@ -13394,7 +13778,7 @@ async function archiveCandidate(rootDir, target) {
13394
13778
  const { paths } = await loadVaultConfig(rootDir);
13395
13779
  const graph = await readJsonFile(paths.graphPath);
13396
13780
  const candidate = resolveCandidateTarget(graph?.pages ?? [], target);
13397
- await fs17.rm(path21.join(paths.wikiDir, candidate.path), { force: true });
13781
+ await fs18.rm(path22.join(paths.wikiDir, candidate.path), { force: true });
13398
13782
  const nextPages = sortGraphPages((graph?.pages ?? []).filter((page) => page.id !== candidate.id && page.path !== candidate.path));
13399
13783
  const nextGraph = {
13400
13784
  generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -13433,18 +13817,18 @@ async function archiveCandidate(rootDir, target) {
13433
13817
  }
13434
13818
  async function ensureObsidianWorkspace(rootDir) {
13435
13819
  const { config } = await loadVaultConfig(rootDir);
13436
- const obsidianDir = path21.join(rootDir, ".obsidian");
13820
+ const obsidianDir = path22.join(rootDir, ".obsidian");
13437
13821
  const projectIds = projectEntries(config).map((project) => project.id);
13438
13822
  await ensureDir(obsidianDir);
13439
13823
  await Promise.all([
13440
- writeJsonFile(path21.join(obsidianDir, "app.json"), {
13824
+ writeJsonFile(path22.join(obsidianDir, "app.json"), {
13441
13825
  alwaysUpdateLinks: true,
13442
13826
  newFileLocation: "folder",
13443
13827
  newFileFolderPath: "wiki/insights",
13444
13828
  useMarkdownLinks: false,
13445
13829
  attachmentFolderPath: "raw/assets"
13446
13830
  }),
13447
- writeJsonFile(path21.join(obsidianDir, "core-plugins.json"), [
13831
+ writeJsonFile(path22.join(obsidianDir, "core-plugins.json"), [
13448
13832
  "file-explorer",
13449
13833
  "global-search",
13450
13834
  "switcher",
@@ -13454,7 +13838,7 @@ async function ensureObsidianWorkspace(rootDir) {
13454
13838
  "tag-pane",
13455
13839
  "page-preview"
13456
13840
  ]),
13457
- writeJsonFile(path21.join(obsidianDir, "graph.json"), {
13841
+ writeJsonFile(path22.join(obsidianDir, "graph.json"), {
13458
13842
  "collapse-filter": false,
13459
13843
  search: "",
13460
13844
  showTags: true,
@@ -13466,7 +13850,7 @@ async function ensureObsidianWorkspace(rootDir) {
13466
13850
  })),
13467
13851
  localJumps: false
13468
13852
  }),
13469
- writeJsonFile(path21.join(obsidianDir, "workspace.json"), {
13853
+ writeJsonFile(path22.join(obsidianDir, "workspace.json"), {
13470
13854
  active: "root",
13471
13855
  lastOpenFiles: ["wiki/index.md", "wiki/projects/index.md", "wiki/candidates/index.md", "wiki/insights/index.md"],
13472
13856
  left: {
@@ -13481,7 +13865,7 @@ async function ensureObsidianWorkspace(rootDir) {
13481
13865
  async function initVault(rootDir, options = {}) {
13482
13866
  const { paths } = await initWorkspace(rootDir);
13483
13867
  await installConfiguredAgents(rootDir);
13484
- const insightsIndexPath = path21.join(paths.wikiDir, "insights", "index.md");
13868
+ const insightsIndexPath = path22.join(paths.wikiDir, "insights", "index.md");
13485
13869
  const now = (/* @__PURE__ */ new Date()).toISOString();
13486
13870
  await writeFileIfChanged(
13487
13871
  insightsIndexPath,
@@ -13517,7 +13901,7 @@ async function initVault(rootDir, options = {}) {
13517
13901
  )
13518
13902
  );
13519
13903
  await writeFileIfChanged(
13520
- path21.join(paths.wikiDir, "projects", "index.md"),
13904
+ path22.join(paths.wikiDir, "projects", "index.md"),
13521
13905
  matter9.stringify(["# Projects", "", "- Run `swarmvault compile` to build project rollups.", ""].join("\n"), {
13522
13906
  page_id: "projects:index",
13523
13907
  kind: "index",
@@ -13539,7 +13923,7 @@ async function initVault(rootDir, options = {}) {
13539
13923
  })
13540
13924
  );
13541
13925
  await writeFileIfChanged(
13542
- path21.join(paths.wikiDir, "candidates", "index.md"),
13926
+ path22.join(paths.wikiDir, "candidates", "index.md"),
13543
13927
  matter9.stringify(["# Candidates", "", "- Run `swarmvault compile` to stage candidate pages.", ""].join("\n"), {
13544
13928
  page_id: "candidates:index",
13545
13929
  kind: "index",
@@ -13678,7 +14062,7 @@ async function compileVault(rootDir, options = {}) {
13678
14062
  ),
13679
14063
  Promise.all(
13680
14064
  clean.map(async (manifest) => {
13681
- const cached = await readJsonFile(path21.join(paths.analysesDir, `${manifest.sourceId}.json`));
14065
+ const cached = await readJsonFile(path22.join(paths.analysesDir, `${manifest.sourceId}.json`));
13682
14066
  if (cached) {
13683
14067
  analysisProgress.tick(manifest.title);
13684
14068
  return cached;
@@ -13706,22 +14090,22 @@ async function compileVault(rootDir, options = {}) {
13706
14090
  }
13707
14091
  const enriched = enrichResolvedCodeImports(manifest, analysis, codeIndex);
13708
14092
  if (analysisSignature(enriched) !== analysisSignature(analysis)) {
13709
- await writeJsonFile(path21.join(paths.analysesDir, `${analysis.sourceId}.json`), enriched);
14093
+ await writeJsonFile(path22.join(paths.analysesDir, `${analysis.sourceId}.json`), enriched);
13710
14094
  }
13711
14095
  return enriched;
13712
14096
  })
13713
14097
  );
13714
14098
  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"))
14099
+ ensureDir(path22.join(paths.wikiDir, "sources")),
14100
+ ensureDir(path22.join(paths.wikiDir, "code")),
14101
+ ensureDir(path22.join(paths.wikiDir, "concepts")),
14102
+ ensureDir(path22.join(paths.wikiDir, "entities")),
14103
+ ensureDir(path22.join(paths.wikiDir, "outputs")),
14104
+ ensureDir(path22.join(paths.wikiDir, "projects")),
14105
+ ensureDir(path22.join(paths.wikiDir, "insights")),
14106
+ ensureDir(path22.join(paths.wikiDir, "candidates")),
14107
+ ensureDir(path22.join(paths.wikiDir, "candidates", "concepts")),
14108
+ ensureDir(path22.join(paths.wikiDir, "candidates", "entities"))
13725
14109
  ]);
13726
14110
  const sync = await syncVaultArtifacts(rootDir, {
13727
14111
  schemas,
@@ -13868,7 +14252,7 @@ async function queryVault(rootDir, options) {
13868
14252
  assetFiles: staged.assetFiles
13869
14253
  }
13870
14254
  ]);
13871
- stagedPath = path21.join(approval.approvalDir, "wiki", staged.page.path);
14255
+ stagedPath = path22.join(approval.approvalDir, "wiki", staged.page.path);
13872
14256
  savedPageId = staged.page.id;
13873
14257
  approvalId = approval.approvalId;
13874
14258
  approvalDir = approval.approvalDir;
@@ -14124,9 +14508,9 @@ ${orchestrationNotes.join("\n")}
14124
14508
  approvalId = approval.approvalId;
14125
14509
  approvalDir = approval.approvalDir;
14126
14510
  stepResults.forEach((result, index) => {
14127
- result.stagedPath = path21.join(approval.approvalDir, "wiki", stagedStepPages[index]?.page.path ?? "");
14511
+ result.stagedPath = path22.join(approval.approvalDir, "wiki", stagedStepPages[index]?.page.path ?? "");
14128
14512
  });
14129
- stagedHubPath = path21.join(approval.approvalDir, "wiki", hubPage.path);
14513
+ stagedHubPath = path22.join(approval.approvalDir, "wiki", hubPage.path);
14130
14514
  } else {
14131
14515
  await refreshVaultAfterOutputSave(rootDir);
14132
14516
  }
@@ -14213,11 +14597,11 @@ async function benchmarkVault(rootDir, options = {}) {
14213
14597
  }
14214
14598
  }
14215
14599
  for (const page of graph.pages) {
14216
- const absolutePath = path21.join(paths.wikiDir, page.path);
14600
+ const absolutePath = path22.join(paths.wikiDir, page.path);
14217
14601
  if (!await fileExists(absolutePath)) {
14218
14602
  continue;
14219
14603
  }
14220
- const parsed = matter9(await fs17.readFile(absolutePath, "utf8"));
14604
+ const parsed = matter9(await fs18.readFile(absolutePath, "utf8"));
14221
14605
  pageContentsById.set(page.id, parsed.content);
14222
14606
  }
14223
14607
  const configuredQuestions = (config.benchmark?.questions ?? []).map((question) => normalizeWhitespace(question)).filter(Boolean);
@@ -14262,7 +14646,7 @@ async function listGraphHyperedges(rootDir, target, limit = 25) {
14262
14646
  }
14263
14647
  async function readGraphReport(rootDir) {
14264
14648
  const { paths } = await loadVaultConfig(rootDir);
14265
- return readJsonFile(path21.join(paths.wikiDir, "graph", "report.json"));
14649
+ return readJsonFile(path22.join(paths.wikiDir, "graph", "report.json"));
14266
14650
  }
14267
14651
  async function listGodNodes(rootDir, limit = 10) {
14268
14652
  const graph = await ensureCompiledGraph(rootDir);
@@ -14275,15 +14659,15 @@ async function listPages(rootDir) {
14275
14659
  }
14276
14660
  async function readPage(rootDir, relativePath) {
14277
14661
  const { paths } = await loadVaultConfig(rootDir);
14278
- const absolutePath = path21.resolve(paths.wikiDir, relativePath);
14662
+ const absolutePath = path22.resolve(paths.wikiDir, relativePath);
14279
14663
  if (!absolutePath.startsWith(paths.wikiDir) || !await fileExists(absolutePath)) {
14280
14664
  return null;
14281
14665
  }
14282
- const raw = await fs17.readFile(absolutePath, "utf8");
14666
+ const raw = await fs18.readFile(absolutePath, "utf8");
14283
14667
  const parsed = matter9(raw);
14284
14668
  return {
14285
14669
  path: relativePath,
14286
- title: typeof parsed.data.title === "string" ? parsed.data.title : path21.basename(relativePath, path21.extname(relativePath)),
14670
+ title: typeof parsed.data.title === "string" ? parsed.data.title : path22.basename(relativePath, path22.extname(relativePath)),
14287
14671
  frontmatter: parsed.data,
14288
14672
  content: parsed.content
14289
14673
  };
@@ -14319,7 +14703,7 @@ function structuralLintFindings(_rootDir, paths, graph, schemas, manifests, sour
14319
14703
  severity: "warning",
14320
14704
  code: "stale_page",
14321
14705
  message: `Page ${page.title} is stale because the vault schema changed.`,
14322
- pagePath: path21.join(paths.wikiDir, page.path),
14706
+ pagePath: path22.join(paths.wikiDir, page.path),
14323
14707
  relatedPageIds: [page.id]
14324
14708
  });
14325
14709
  }
@@ -14330,7 +14714,7 @@ function structuralLintFindings(_rootDir, paths, graph, schemas, manifests, sour
14330
14714
  severity: "warning",
14331
14715
  code: "stale_page",
14332
14716
  message: `Page ${page.title} is stale because source ${sourceId} changed.`,
14333
- pagePath: path21.join(paths.wikiDir, page.path),
14717
+ pagePath: path22.join(paths.wikiDir, page.path),
14334
14718
  relatedSourceIds: [sourceId],
14335
14719
  relatedPageIds: [page.id]
14336
14720
  });
@@ -14341,13 +14725,13 @@ function structuralLintFindings(_rootDir, paths, graph, schemas, manifests, sour
14341
14725
  severity: "info",
14342
14726
  code: "orphan_page",
14343
14727
  message: `Page ${page.title} has no backlinks.`,
14344
- pagePath: path21.join(paths.wikiDir, page.path),
14728
+ pagePath: path22.join(paths.wikiDir, page.path),
14345
14729
  relatedPageIds: [page.id]
14346
14730
  });
14347
14731
  }
14348
- const absolutePath = path21.join(paths.wikiDir, page.path);
14732
+ const absolutePath = path22.join(paths.wikiDir, page.path);
14349
14733
  if (await fileExists(absolutePath)) {
14350
- const content = await fs17.readFile(absolutePath, "utf8");
14734
+ const content = await fs18.readFile(absolutePath, "utf8");
14351
14735
  if (content.includes("## Claims")) {
14352
14736
  const uncited = content.split("\n").filter((line) => line.startsWith("- ") && !line.includes("[source:"));
14353
14737
  if (uncited.length) {
@@ -14464,7 +14848,7 @@ async function bootstrapDemo(rootDir, input) {
14464
14848
  }
14465
14849
 
14466
14850
  // src/mcp.ts
14467
- var SERVER_VERSION = "0.1.33";
14851
+ var SERVER_VERSION = "0.2.1";
14468
14852
  async function createMcpServer(rootDir) {
14469
14853
  const server = new McpServer({
14470
14854
  name: "swarmvault",
@@ -14735,7 +15119,7 @@ async function createMcpServer(rootDir) {
14735
15119
  },
14736
15120
  async () => {
14737
15121
  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();
15122
+ const files = (await listFilesRecursive(paths.sessionsDir)).filter((filePath) => filePath.endsWith(".md")).map((filePath) => toPosix(path23.relative(paths.sessionsDir, filePath))).sort();
14739
15123
  return asTextResource("swarmvault://sessions", JSON.stringify(files, null, 2));
14740
15124
  }
14741
15125
  );
@@ -14768,8 +15152,8 @@ async function createMcpServer(rootDir) {
14768
15152
  return asTextResource(`swarmvault://pages/${encodedPath}`, `Page not found: ${relativePath}`);
14769
15153
  }
14770
15154
  const { paths } = await loadVaultConfig(rootDir);
14771
- const absolutePath = path22.resolve(paths.wikiDir, relativePath);
14772
- return asTextResource(`swarmvault://pages/${encodedPath}`, await fs18.readFile(absolutePath, "utf8"));
15155
+ const absolutePath = path23.resolve(paths.wikiDir, relativePath);
15156
+ return asTextResource(`swarmvault://pages/${encodedPath}`, await fs19.readFile(absolutePath, "utf8"));
14773
15157
  }
14774
15158
  );
14775
15159
  server.registerResource(
@@ -14777,11 +15161,11 @@ async function createMcpServer(rootDir) {
14777
15161
  new ResourceTemplate("swarmvault://sessions/{path}", {
14778
15162
  list: async () => {
14779
15163
  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();
15164
+ const files = (await listFilesRecursive(paths.sessionsDir)).filter((filePath) => filePath.endsWith(".md")).map((filePath) => toPosix(path23.relative(paths.sessionsDir, filePath))).sort();
14781
15165
  return {
14782
15166
  resources: files.map((relativePath) => ({
14783
15167
  uri: `swarmvault://sessions/${encodeURIComponent(relativePath)}`,
14784
- name: path22.basename(relativePath, ".md"),
15168
+ name: path23.basename(relativePath, ".md"),
14785
15169
  title: relativePath,
14786
15170
  description: "SwarmVault session artifact",
14787
15171
  mimeType: "text/markdown"
@@ -14798,11 +15182,11 @@ async function createMcpServer(rootDir) {
14798
15182
  const { paths } = await loadVaultConfig(rootDir);
14799
15183
  const encodedPath = typeof variables.path === "string" ? variables.path : "";
14800
15184
  const relativePath = decodeURIComponent(encodedPath);
14801
- const absolutePath = path22.resolve(paths.sessionsDir, relativePath);
15185
+ const absolutePath = path23.resolve(paths.sessionsDir, relativePath);
14802
15186
  if (!absolutePath.startsWith(paths.sessionsDir) || !await fileExists(absolutePath)) {
14803
15187
  return asTextResource(`swarmvault://sessions/${encodedPath}`, `Session not found: ${relativePath}`);
14804
15188
  }
14805
- return asTextResource(`swarmvault://sessions/${encodedPath}`, await fs18.readFile(absolutePath, "utf8"));
15189
+ return asTextResource(`swarmvault://sessions/${encodedPath}`, await fs19.readFile(absolutePath, "utf8"));
14806
15190
  }
14807
15191
  );
14808
15192
  return server;
@@ -14850,13 +15234,13 @@ function asTextResource(uri, text) {
14850
15234
  }
14851
15235
 
14852
15236
  // src/schedule.ts
14853
- import fs19 from "fs/promises";
14854
- import path23 from "path";
15237
+ import fs20 from "fs/promises";
15238
+ import path24 from "path";
14855
15239
  function scheduleStatePath(schedulesDir, jobId) {
14856
- return path23.join(schedulesDir, `${encodeURIComponent(jobId)}.json`);
15240
+ return path24.join(schedulesDir, `${encodeURIComponent(jobId)}.json`);
14857
15241
  }
14858
15242
  function scheduleLockPath(schedulesDir, jobId) {
14859
- return path23.join(schedulesDir, `${encodeURIComponent(jobId)}.lock`);
15243
+ return path24.join(schedulesDir, `${encodeURIComponent(jobId)}.lock`);
14860
15244
  }
14861
15245
  function parseEveryDuration(value) {
14862
15246
  const match = value.trim().match(/^(\d+)(m|h|d)$/i);
@@ -14959,13 +15343,13 @@ async function acquireJobLease(rootDir, jobId) {
14959
15343
  const { paths } = await loadVaultConfig(rootDir);
14960
15344
  const leasePath = scheduleLockPath(paths.schedulesDir, jobId);
14961
15345
  await ensureDir(paths.schedulesDir);
14962
- const handle = await fs19.open(leasePath, "wx");
15346
+ const handle = await fs20.open(leasePath, "wx");
14963
15347
  await handle.writeFile(`${process.pid}
14964
15348
  ${(/* @__PURE__ */ new Date()).toISOString()}
14965
15349
  `);
14966
15350
  await handle.close();
14967
15351
  return async () => {
14968
- await fs19.rm(leasePath, { force: true });
15352
+ await fs20.rm(leasePath, { force: true });
14969
15353
  };
14970
15354
  }
14971
15355
  async function listSchedules(rootDir) {
@@ -15111,33 +15495,750 @@ async function serveSchedules(rootDir, pollMs = 3e4) {
15111
15495
  };
15112
15496
  }
15113
15497
 
15498
+ // src/sources.ts
15499
+ import { spawn as spawn2 } from "child_process";
15500
+ import fs21 from "fs/promises";
15501
+ import path25 from "path";
15502
+ import { JSDOM as JSDOM3 } from "jsdom";
15503
+ var DEFAULT_CRAWL_MAX_PAGES = 12;
15504
+ var DEFAULT_CRAWL_MAX_DEPTH = 2;
15505
+ var DOCS_HINT_SEGMENTS = /* @__PURE__ */ new Set([
15506
+ "docs",
15507
+ "documentation",
15508
+ "wiki",
15509
+ "help",
15510
+ "reference",
15511
+ "references",
15512
+ "guide",
15513
+ "guides",
15514
+ "tutorial",
15515
+ "tutorials",
15516
+ "manual",
15517
+ "api",
15518
+ "apis",
15519
+ "getting-started"
15520
+ ]);
15521
+ function uniqueStrings4(values) {
15522
+ return uniqueBy(values.filter(Boolean), (value) => value);
15523
+ }
15524
+ function normalizeManagedStatus(value) {
15525
+ return value === "missing" || value === "error" ? value : "ready";
15526
+ }
15527
+ function withinRoot2(rootPath, targetPath) {
15528
+ const relative = path25.relative(rootPath, targetPath);
15529
+ return relative === "" || !relative.startsWith("..") && !path25.isAbsolute(relative);
15530
+ }
15531
+ async function findNearestGitRoot3(startPath) {
15532
+ let current = path25.resolve(startPath);
15533
+ try {
15534
+ const stat = await fs21.stat(current);
15535
+ if (!stat.isDirectory()) {
15536
+ current = path25.dirname(current);
15537
+ }
15538
+ } catch {
15539
+ current = path25.dirname(current);
15540
+ }
15541
+ while (true) {
15542
+ if (await fileExists(path25.join(current, ".git"))) {
15543
+ return current;
15544
+ }
15545
+ const parent = path25.dirname(current);
15546
+ if (parent === current) {
15547
+ return null;
15548
+ }
15549
+ current = parent;
15550
+ }
15551
+ }
15552
+ function normalizeUrlWithoutHash(input) {
15553
+ const url = new URL(input);
15554
+ url.hash = "";
15555
+ if (url.protocol === "http:" && url.port === "80" || url.protocol === "https:" && url.port === "443") {
15556
+ url.port = "";
15557
+ }
15558
+ return url.toString();
15559
+ }
15560
+ function normalizeGitHubRepoRootUrl(input) {
15561
+ let parsed;
15562
+ try {
15563
+ parsed = new URL(input);
15564
+ } catch {
15565
+ return null;
15566
+ }
15567
+ const host = parsed.hostname.toLowerCase();
15568
+ if (host !== "github.com" && host !== "www.github.com") {
15569
+ return null;
15570
+ }
15571
+ const segments = parsed.pathname.split("/").map((segment) => segment.trim()).filter(Boolean);
15572
+ if (segments.length !== 2) {
15573
+ return null;
15574
+ }
15575
+ const [owner, repoSegment] = segments;
15576
+ const repo = repoSegment.replace(/\.git$/i, "");
15577
+ if (!owner || !repo) {
15578
+ return null;
15579
+ }
15580
+ const url = `https://github.com/${owner}/${repo}`;
15581
+ return {
15582
+ url,
15583
+ cloneUrl: `${url}.git`,
15584
+ title: `${owner}/${repo}`
15585
+ };
15586
+ }
15587
+ function looksLikeDocsPathname(pathname) {
15588
+ const segments = pathname.split("/").map((segment) => segment.trim().toLowerCase()).filter(Boolean);
15589
+ return segments.some((segment) => DOCS_HINT_SEGMENTS.has(segment));
15590
+ }
15591
+ function isLikelyDocsStartUrl(url) {
15592
+ return looksLikeDocsPathname(url.pathname) || url.hostname.toLowerCase().startsWith("docs.");
15593
+ }
15594
+ function normalizeCrawlCandidate(href, baseUrl) {
15595
+ try {
15596
+ const url = new URL(href, baseUrl);
15597
+ if (!["http:", "https:"].includes(url.protocol)) {
15598
+ return null;
15599
+ }
15600
+ if (url.hash) {
15601
+ url.hash = "";
15602
+ }
15603
+ return url.toString();
15604
+ } catch {
15605
+ return null;
15606
+ }
15607
+ }
15608
+ function pathPrefix(pathname) {
15609
+ const segments = pathname.split("/").filter(Boolean);
15610
+ if (segments.length === 0) {
15611
+ return "/";
15612
+ }
15613
+ return `/${segments[0]}`;
15614
+ }
15615
+ function isAllowedDocsCandidate(candidate, startUrl) {
15616
+ if (candidate.origin !== startUrl.origin) {
15617
+ return false;
15618
+ }
15619
+ const extension = path25.extname(candidate.pathname).toLowerCase();
15620
+ if (extension && extension !== ".html" && extension !== ".htm" && extension !== ".md") {
15621
+ return false;
15622
+ }
15623
+ if (looksLikeDocsPathname(candidate.pathname)) {
15624
+ return true;
15625
+ }
15626
+ const startPrefix = pathPrefix(startUrl.pathname);
15627
+ const candidatePrefix = pathPrefix(candidate.pathname);
15628
+ return startPrefix !== "/" && candidatePrefix === startPrefix;
15629
+ }
15630
+ async function fetchHtml(url) {
15631
+ await validateUrlSafety(url);
15632
+ const response = await fetch(url);
15633
+ if (!response.ok) {
15634
+ throw new Error(`Failed to fetch ${url}: ${response.status} ${response.statusText}`);
15635
+ }
15636
+ const contentType = response.headers.get("content-type")?.split(";")[0]?.trim() ?? "text/html";
15637
+ if (!contentType.includes("html")) {
15638
+ throw new Error(`Unsupported docs crawl content type at ${url}: ${contentType}`);
15639
+ }
15640
+ const html = await response.text();
15641
+ const dom = new JSDOM3(html, { url });
15642
+ const document = dom.window.document;
15643
+ const title = document.title.trim() || url;
15644
+ const links = [...document.querySelectorAll("a[href]")].map((anchor) => normalizeCrawlCandidate(anchor.getAttribute("href") ?? "", url)).filter((value) => Boolean(value));
15645
+ return { title, links };
15646
+ }
15647
+ async function crawlDocsSource(url, maxPages, maxDepth) {
15648
+ const startUrl = new URL(normalizeUrlWithoutHash(url));
15649
+ const initial = await fetchHtml(startUrl.toString());
15650
+ const sameDomainDocsLinks = uniqueStrings4(
15651
+ initial.links.filter((candidate) => {
15652
+ const parsed = new URL(candidate);
15653
+ return isAllowedDocsCandidate(parsed, startUrl);
15654
+ })
15655
+ );
15656
+ if (!isLikelyDocsStartUrl(startUrl) && sameDomainDocsLinks.length < 3) {
15657
+ throw new Error(
15658
+ "This URL does not look like a docs hub. Use `swarmvault add` for single articles or `swarmvault ingest` for direct files."
15659
+ );
15660
+ }
15661
+ const visited = /* @__PURE__ */ new Set();
15662
+ const queued = /* @__PURE__ */ new Set();
15663
+ const pages = [];
15664
+ const queue = [{ url: startUrl.toString(), depth: 0 }];
15665
+ queued.add(startUrl.toString());
15666
+ while (queue.length > 0 && pages.length < maxPages) {
15667
+ const current = queue.shift();
15668
+ if (!current) {
15669
+ continue;
15670
+ }
15671
+ if (visited.has(current.url)) {
15672
+ continue;
15673
+ }
15674
+ visited.add(current.url);
15675
+ pages.push(current.url);
15676
+ if (current.depth >= maxDepth) {
15677
+ continue;
15678
+ }
15679
+ const { links } = await fetchHtml(current.url);
15680
+ for (const candidate of links) {
15681
+ if (pages.length + queue.length >= maxPages) {
15682
+ break;
15683
+ }
15684
+ if (queued.has(candidate) || visited.has(candidate)) {
15685
+ continue;
15686
+ }
15687
+ const parsed = new URL(candidate);
15688
+ if (!isAllowedDocsCandidate(parsed, startUrl)) {
15689
+ continue;
15690
+ }
15691
+ queued.add(candidate);
15692
+ queue.push({ url: candidate, depth: current.depth + 1 });
15693
+ }
15694
+ }
15695
+ return {
15696
+ title: initial.title,
15697
+ pages
15698
+ };
15699
+ }
15700
+ function stableManagedSourceId(kind, raw, fallbackTitle) {
15701
+ return `${kind}-${slugify(fallbackTitle)}-${sha256(raw).slice(0, 8)}`;
15702
+ }
15703
+ function matchesManagedSourceSpec(existing, input) {
15704
+ if (existing.kind !== input.kind) {
15705
+ return false;
15706
+ }
15707
+ if (input.kind === "directory") {
15708
+ return path25.resolve(existing.path ?? "") === path25.resolve(input.path);
15709
+ }
15710
+ return (existing.url ?? "") === input.url;
15711
+ }
15712
+ async function resolveManagedSourceInput(rootDir, input) {
15713
+ const absoluteInput = path25.resolve(rootDir, input);
15714
+ if (!(input.startsWith("http://") || input.startsWith("https://"))) {
15715
+ const stat = await fs21.stat(absoluteInput).catch(() => null);
15716
+ if (!stat) {
15717
+ throw new Error(`Source not found: ${input}`);
15718
+ }
15719
+ if (!stat.isDirectory()) {
15720
+ throw new Error(
15721
+ "`swarmvault source add` supports directories, public GitHub repo root URLs, and docs hubs. Use `swarmvault ingest` for single files."
15722
+ );
15723
+ }
15724
+ const detectedRepoRoot = await findNearestGitRoot3(absoluteInput);
15725
+ const repoRoot = detectedRepoRoot && !(withinRoot2(rootDir, absoluteInput) && !withinRoot2(rootDir, detectedRepoRoot)) ? detectedRepoRoot : absoluteInput;
15726
+ return {
15727
+ kind: "directory",
15728
+ path: absoluteInput,
15729
+ repoRoot,
15730
+ title: path25.basename(absoluteInput) || absoluteInput
15731
+ };
15732
+ }
15733
+ const github = normalizeGitHubRepoRootUrl(input);
15734
+ if (github) {
15735
+ return {
15736
+ kind: "github_repo",
15737
+ ...github
15738
+ };
15739
+ }
15740
+ const parsed = new URL(input);
15741
+ if (parsed.hostname.toLowerCase().includes("github.com")) {
15742
+ throw new Error(
15743
+ "`swarmvault source add` only supports public GitHub repo root URLs. Use a repo root like https://github.com/owner/repo."
15744
+ );
15745
+ }
15746
+ return {
15747
+ kind: "crawl_url",
15748
+ url: normalizeUrlWithoutHash(input),
15749
+ title: parsed.hostname
15750
+ };
15751
+ }
15752
+ function directorySourceIdsFor(manifests, inputPath) {
15753
+ return manifests.filter((manifest) => manifest.originalPath && withinRoot2(path25.resolve(inputPath), path25.resolve(manifest.originalPath))).map((manifest) => manifest.sourceId).sort((left, right) => left.localeCompare(right));
15754
+ }
15755
+ async function syncDirectorySource(rootDir, inputPath, repoRoot) {
15756
+ const manifestsBefore = await listManifests(rootDir);
15757
+ const previousInScope = manifestsBefore.filter(
15758
+ (manifest) => manifest.originalPath && withinRoot2(path25.resolve(inputPath), path25.resolve(manifest.originalPath))
15759
+ );
15760
+ const result = await ingestDirectory(rootDir, inputPath, { repoRoot });
15761
+ const removed = [];
15762
+ for (const manifest of previousInScope) {
15763
+ if (!manifest.originalPath) {
15764
+ continue;
15765
+ }
15766
+ if (await fileExists(path25.resolve(manifest.originalPath))) {
15767
+ continue;
15768
+ }
15769
+ const removedManifest = await removeManifestBySourceId(rootDir, manifest.sourceId);
15770
+ if (removedManifest) {
15771
+ removed.push(removedManifest.sourceId);
15772
+ }
15773
+ }
15774
+ const manifestsAfter = await listManifests(rootDir);
15775
+ return {
15776
+ title: path25.basename(inputPath) || inputPath,
15777
+ sourceIds: directorySourceIdsFor(manifestsAfter, inputPath),
15778
+ counts: {
15779
+ scannedCount: result.scannedCount,
15780
+ importedCount: result.imported.length,
15781
+ updatedCount: result.updated.length,
15782
+ removedCount: removed.length,
15783
+ skippedCount: result.skipped.length
15784
+ },
15785
+ changed: result.imported.length + result.updated.length + removed.length > 0
15786
+ };
15787
+ }
15788
+ async function runGitCommand(cwd, args) {
15789
+ await new Promise((resolve, reject) => {
15790
+ const child = spawn2("git", args, {
15791
+ cwd,
15792
+ stdio: ["ignore", "pipe", "pipe"]
15793
+ });
15794
+ let stderr = "";
15795
+ child.stderr.on("data", (chunk) => {
15796
+ stderr += chunk.toString("utf8");
15797
+ });
15798
+ child.on("error", reject);
15799
+ child.on("close", (code) => {
15800
+ if (code === 0) {
15801
+ resolve();
15802
+ return;
15803
+ }
15804
+ reject(new Error(stderr.trim() || `git ${args.join(" ")} failed with exit code ${code ?? 1}`));
15805
+ });
15806
+ });
15807
+ }
15808
+ async function syncGitHubRepoSource(rootDir, entry) {
15809
+ const workingDir = await managedSourceWorkingDir(rootDir, entry.id);
15810
+ const checkoutDir = path25.join(workingDir, "checkout");
15811
+ await fs21.rm(checkoutDir, { recursive: true, force: true });
15812
+ await ensureDir(workingDir);
15813
+ if (!entry.url) {
15814
+ throw new Error(`Managed source ${entry.id} is missing its repository URL.`);
15815
+ }
15816
+ const github = normalizeGitHubRepoRootUrl(entry.url);
15817
+ if (!github) {
15818
+ throw new Error(`Managed source ${entry.id} has an invalid GitHub repo URL.`);
15819
+ }
15820
+ await runGitCommand(workingDir, ["clone", "--depth", "1", github.cloneUrl, "checkout"]);
15821
+ return await syncDirectorySource(rootDir, checkoutDir, checkoutDir);
15822
+ }
15823
+ async function syncCrawlSource(rootDir, entry, options) {
15824
+ if (!entry.url) {
15825
+ throw new Error(`Managed source ${entry.id} is missing its URL.`);
15826
+ }
15827
+ const crawl = await crawlDocsSource(entry.url, options.maxPages ?? DEFAULT_CRAWL_MAX_PAGES, options.maxDepth ?? DEFAULT_CRAWL_MAX_DEPTH);
15828
+ const previousSourceIds = [...entry.sourceIds];
15829
+ const currentSourceIds = [];
15830
+ let importedCount = 0;
15831
+ let updatedCount = 0;
15832
+ for (const pageUrl of crawl.pages) {
15833
+ const persisted = await ingestInputDetailed(rootDir, pageUrl);
15834
+ currentSourceIds.push(persisted.manifest.sourceId);
15835
+ if (persisted.isNew) {
15836
+ importedCount += 1;
15837
+ } else if (persisted.wasUpdated) {
15838
+ updatedCount += 1;
15839
+ }
15840
+ }
15841
+ let removedCount = 0;
15842
+ for (const sourceId of previousSourceIds) {
15843
+ if (currentSourceIds.includes(sourceId)) {
15844
+ continue;
15845
+ }
15846
+ if (await removeManifestBySourceId(rootDir, sourceId)) {
15847
+ removedCount += 1;
15848
+ }
15849
+ }
15850
+ return {
15851
+ title: crawl.title,
15852
+ sourceIds: uniqueStrings4(currentSourceIds).sort((left, right) => left.localeCompare(right)),
15853
+ counts: {
15854
+ scannedCount: crawl.pages.length,
15855
+ importedCount,
15856
+ updatedCount,
15857
+ removedCount,
15858
+ skippedCount: 0
15859
+ },
15860
+ changed: importedCount + updatedCount + removedCount > 0
15861
+ };
15862
+ }
15863
+ async function syncManagedSource(rootDir, entry, options) {
15864
+ const now = (/* @__PURE__ */ new Date()).toISOString();
15865
+ try {
15866
+ let sync;
15867
+ if (entry.kind === "directory") {
15868
+ if (!entry.path || !entry.repoRoot) {
15869
+ throw new Error(`Managed source ${entry.id} is missing its directory path.`);
15870
+ }
15871
+ if (!await fileExists(entry.path)) {
15872
+ return {
15873
+ ...entry,
15874
+ status: "missing",
15875
+ updatedAt: now,
15876
+ lastSyncAt: now,
15877
+ lastSyncStatus: "error",
15878
+ lastError: `Directory not found: ${entry.path}`,
15879
+ changed: false
15880
+ };
15881
+ }
15882
+ sync = await syncDirectorySource(rootDir, entry.path, entry.repoRoot);
15883
+ } else if (entry.kind === "github_repo") {
15884
+ sync = await syncGitHubRepoSource(rootDir, entry);
15885
+ } else {
15886
+ sync = await syncCrawlSource(rootDir, entry, options);
15887
+ }
15888
+ return {
15889
+ ...entry,
15890
+ title: sync.title || entry.title,
15891
+ sourceIds: sync.sourceIds,
15892
+ status: "ready",
15893
+ updatedAt: now,
15894
+ lastSyncAt: now,
15895
+ lastSyncStatus: "success",
15896
+ lastSyncCounts: sync.counts,
15897
+ lastError: void 0,
15898
+ changed: sync.changed
15899
+ };
15900
+ } catch (error) {
15901
+ return {
15902
+ ...entry,
15903
+ status: normalizeManagedStatus(entry.status),
15904
+ updatedAt: now,
15905
+ lastSyncAt: now,
15906
+ lastSyncStatus: "error",
15907
+ lastError: error instanceof Error ? error.message : String(error),
15908
+ changed: false
15909
+ };
15910
+ }
15911
+ }
15912
+ function scopedSourcePages(graph, sourceIds) {
15913
+ const scopedSet = new Set(sourceIds);
15914
+ return graph.pages.filter((page) => page.sourceIds.some((sourceId) => scopedSet.has(sourceId)));
15915
+ }
15916
+ function scopedNodeIds(graph, sourceIds) {
15917
+ const scopedSet = new Set(sourceIds);
15918
+ return graph.nodes.filter((node) => node.sourceIds.some((sourceId) => scopedSet.has(sourceId))).map((node) => node.id);
15919
+ }
15920
+ async function loadSourceAnalyses(rootDir, sourceIds) {
15921
+ const { paths } = await loadVaultConfig(rootDir);
15922
+ const analyses = await Promise.all(
15923
+ sourceIds.map(async (sourceId) => await readJsonFile(path25.join(paths.analysesDir, `${sourceId}.json`)))
15924
+ );
15925
+ return analyses.filter((analysis) => Boolean(analysis?.sourceId));
15926
+ }
15927
+ function renderDeterministicSourceBrief(input) {
15928
+ const modulePages = input.sourcePages.filter((page) => page.kind === "module").slice(0, 6);
15929
+ const sourcePages = input.sourcePages.filter((page) => page.kind === "source").slice(0, 6);
15930
+ const conceptPages = input.sourcePages.filter((page) => page.kind === "concept").slice(0, 6);
15931
+ const entityPages = input.sourcePages.filter((page) => page.kind === "entity").slice(0, 6);
15932
+ const questions = uniqueStrings4(input.analyses.flatMap((analysis) => analysis.questions)).slice(0, 5);
15933
+ const summary = truncate(
15934
+ normalizeWhitespace(
15935
+ uniqueStrings4(input.analyses.map((analysis) => analysis.summary).filter(Boolean)).join(" ") || `${input.source.title} has been compiled into a local source graph.`
15936
+ ),
15937
+ 320
15938
+ );
15939
+ const scopedNodeIdSet = new Set(scopedNodeIds(input.graph, input.source.sourceIds));
15940
+ const surprises = input.report?.surprisingConnections.filter((connection) => scopedNodeIdSet.has(connection.sourceNodeId) || scopedNodeIdSet.has(connection.targetNodeId)).slice(0, 4) ?? [];
15941
+ const contradictions = input.report?.contradictions.filter(
15942
+ (contradiction) => input.source.sourceIds.includes(contradiction.sourceIdA) || input.source.sourceIds.includes(contradiction.sourceIdB)
15943
+ ) ?? [];
15944
+ return [
15945
+ `# Source Brief: ${input.source.title}`,
15946
+ "",
15947
+ "## What This Source Is",
15948
+ "",
15949
+ summary,
15950
+ "",
15951
+ "## Read First",
15952
+ "",
15953
+ ...sourcePages.length ? sourcePages.map((page) => `- [[${page.path.replace(/\.md$/, "")}|${page.title}]]`) : ["- No source page links are available yet."],
15954
+ "",
15955
+ "## Core Pages",
15956
+ "",
15957
+ ...modulePages.length ? modulePages.map((page) => `- [[${page.path.replace(/\.md$/, "")}|${page.title}]]`) : ["- No module pages are available yet."],
15958
+ ...conceptPages.length ? ["", "Concept pages:", ...conceptPages.map((page) => `- [[${page.path.replace(/\.md$/, "")}|${page.title}]]`)] : [],
15959
+ ...entityPages.length ? ["", "Entity pages:", ...entityPages.map((page) => `- [[${page.path.replace(/\.md$/, "")}|${page.title}]]`)] : [],
15960
+ "",
15961
+ "## How The Important Parts Fit Together",
15962
+ "",
15963
+ `- Compiled source pages: ${sourcePages.length}`,
15964
+ `- Module pages: ${modulePages.length}`,
15965
+ `- Graph nodes touching this source: ${scopedNodeIdSet.size}`,
15966
+ `- Current tracked source ids: ${input.source.sourceIds.length}`,
15967
+ "",
15968
+ "## Surprises",
15969
+ "",
15970
+ ...surprises.length ? surprises.map((surprise) => `- ${surprise.explanation}`) : ["- No surprising cross-source connections were highlighted for this source yet."],
15971
+ "",
15972
+ "## Contradictions",
15973
+ "",
15974
+ ...contradictions.length ? contradictions.map(
15975
+ (contradiction) => `- ${contradiction.claimA} / ${contradiction.claimB} (sources: ${contradiction.sourceIdA}, ${contradiction.sourceIdB})`
15976
+ ) : ["- No contradictions were detected for this source."],
15977
+ "",
15978
+ "## Open Questions",
15979
+ "",
15980
+ ...questions.length ? questions.map((question) => `- ${question}`) : ["- No extracted open questions yet."],
15981
+ "",
15982
+ "## Suggested Next Questions",
15983
+ "",
15984
+ ...(input.report?.suggestedQuestions ?? []).slice(0, 5).map((question) => `- ${question}`) || [
15985
+ "- Ask `swarmvault query` about the main modules or sections in this source."
15986
+ ],
15987
+ ""
15988
+ ].join("\n");
15989
+ }
15990
+ async function generateSourceBriefMarkdown(rootDir, source) {
15991
+ const { paths } = await loadVaultConfig(rootDir);
15992
+ const graph = await readJsonFile(paths.graphPath);
15993
+ if (!graph) {
15994
+ return null;
15995
+ }
15996
+ const sourcePages = scopedSourcePages(graph, source.sourceIds);
15997
+ const analyses = await loadSourceAnalyses(rootDir, source.sourceIds);
15998
+ const report = await readGraphReport(rootDir);
15999
+ const fallback = renderDeterministicSourceBrief({
16000
+ source,
16001
+ sourcePages,
16002
+ graph,
16003
+ analyses,
16004
+ report
16005
+ });
16006
+ const provider = await getProviderForTask(rootDir, "queryProvider");
16007
+ if (provider.type === "heuristic") {
16008
+ return fallback;
16009
+ }
16010
+ try {
16011
+ const schemas = await loadVaultSchemas(rootDir);
16012
+ const pageContext = sourcePages.slice(0, 10).map((page) => `- ${page.title} (${page.kind}) -> ${page.path}`).join("\n");
16013
+ const analysisContext = analyses.slice(0, 6).map(
16014
+ (analysis) => `# ${analysis.title}
16015
+ Summary: ${analysis.summary}
16016
+ Questions: ${analysis.questions.join(" | ") || "none"}
16017
+ Concepts: ${analysis.concepts.map((concept) => concept.name).join(", ") || "none"}
16018
+ Entities: ${analysis.entities.map((entity) => entity.name).join(", ") || "none"}`
16019
+ ).join("\n\n---\n\n");
16020
+ const response = await provider.generateText({
16021
+ system: buildSchemaPrompt(
16022
+ schemas.effective.global,
16023
+ "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."
16024
+ ),
16025
+ prompt: [
16026
+ `Source title: ${source.title}`,
16027
+ `Source kind: ${source.kind}`,
16028
+ `Tracked source ids: ${source.sourceIds.join(", ") || "none"}`,
16029
+ "",
16030
+ "Pages:",
16031
+ pageContext || "- none",
16032
+ "",
16033
+ "Analyses:",
16034
+ analysisContext || "No analysis context available.",
16035
+ "",
16036
+ "Deterministic fallback draft:",
16037
+ fallback
16038
+ ].join("\n")
16039
+ });
16040
+ return response.text?.trim() ? response.text.trim() : fallback;
16041
+ } catch {
16042
+ return fallback;
16043
+ }
16044
+ }
16045
+ async function writeSourceBrief(rootDir, source) {
16046
+ if (!source.sourceIds.length) {
16047
+ return null;
16048
+ }
16049
+ const { paths } = await loadVaultConfig(rootDir);
16050
+ const markdown = await generateSourceBriefMarkdown(rootDir, source);
16051
+ if (!markdown) {
16052
+ return null;
16053
+ }
16054
+ const graph = await readJsonFile(paths.graphPath);
16055
+ const relatedPages = graph ? scopedSourcePages(graph, source.sourceIds) : [];
16056
+ const relatedPageIds = relatedPages.slice(0, 12).map((page) => page.id);
16057
+ const relatedNodeIds = graph ? scopedNodeIds(graph, source.sourceIds).slice(0, 20) : [];
16058
+ const projectIds = uniqueStrings4(relatedPages.flatMap((page) => page.projectIds));
16059
+ const now = (/* @__PURE__ */ new Date()).toISOString();
16060
+ const output = buildOutputPage({
16061
+ title: `Source Brief: ${source.title}`,
16062
+ question: `Brief ${source.title}`,
16063
+ answer: markdown,
16064
+ citations: source.sourceIds,
16065
+ schemaHash: graph?.generatedAt ?? "",
16066
+ outputFormat: "report",
16067
+ relatedPageIds,
16068
+ relatedNodeIds,
16069
+ relatedSourceIds: source.sourceIds,
16070
+ projectIds,
16071
+ extraTags: ["source-brief"],
16072
+ origin: "query",
16073
+ slug: `source-briefs/${source.id}`,
16074
+ metadata: {
16075
+ status: "active",
16076
+ createdAt: now,
16077
+ updatedAt: now,
16078
+ compiledFrom: source.sourceIds,
16079
+ managedBy: "system",
16080
+ confidence: 0.82
16081
+ }
16082
+ });
16083
+ const absolutePath = path25.join(paths.wikiDir, output.page.path);
16084
+ await ensureDir(path25.dirname(absolutePath));
16085
+ await fs21.writeFile(absolutePath, output.content, "utf8");
16086
+ return absolutePath;
16087
+ }
16088
+ async function generateBriefsForSources(rootDir, sources) {
16089
+ const briefPaths = /* @__PURE__ */ new Map();
16090
+ for (const source of sources) {
16091
+ const briefPath = await writeSourceBrief(rootDir, source);
16092
+ if (briefPath) {
16093
+ briefPaths.set(source.id, briefPath);
16094
+ }
16095
+ }
16096
+ if (briefPaths.size > 0) {
16097
+ await refreshVaultAfterOutputSave(rootDir);
16098
+ }
16099
+ return briefPaths;
16100
+ }
16101
+ function shouldCompile(changedSources, graphExists, compileRequested) {
16102
+ return compileRequested && (!graphExists || changedSources.length > 0);
16103
+ }
16104
+ async function listManagedSourceRecords(rootDir) {
16105
+ await ensureManagedSourcesArtifact(rootDir);
16106
+ return await loadManagedSources(rootDir);
16107
+ }
16108
+ async function addManagedSource(rootDir, input, options = {}) {
16109
+ const compileRequested = options.compile ?? true;
16110
+ const briefRequested = options.brief ?? true;
16111
+ const sources = await loadManagedSources(rootDir);
16112
+ const resolved = await resolveManagedSourceInput(rootDir, input);
16113
+ const existing = sources.find((candidate) => matchesManagedSourceSpec(candidate, resolved));
16114
+ const now = (/* @__PURE__ */ new Date()).toISOString();
16115
+ const source = existing ?? {
16116
+ id: resolved.kind === "directory" ? stableManagedSourceId("directory", path25.resolve(resolved.path), resolved.title) : stableManagedSourceId(resolved.kind, resolved.url, resolved.title),
16117
+ kind: resolved.kind,
16118
+ title: resolved.title,
16119
+ path: resolved.kind === "directory" ? resolved.path : void 0,
16120
+ repoRoot: resolved.kind === "directory" ? resolved.repoRoot : void 0,
16121
+ url: resolved.kind === "directory" ? void 0 : resolved.url,
16122
+ createdAt: now,
16123
+ updatedAt: now,
16124
+ status: "ready",
16125
+ sourceIds: []
16126
+ };
16127
+ const synced = await syncManagedSource(rootDir, source, options);
16128
+ if (synced.lastSyncStatus === "error") {
16129
+ throw new Error(synced.lastError ?? `Failed to add managed source ${synced.id}.`);
16130
+ }
16131
+ const graphExists = await loadVaultConfig(rootDir).then(({ paths }) => fileExists(paths.graphPath));
16132
+ let compile;
16133
+ if (shouldCompile([synced], graphExists, compileRequested)) {
16134
+ compile = await compileVault(rootDir, {});
16135
+ }
16136
+ let briefGenerated = false;
16137
+ let briefPath;
16138
+ if (compileRequested && briefRequested && synced.status === "ready") {
16139
+ const briefs = await generateBriefsForSources(rootDir, [synced]);
16140
+ briefPath = briefs.get(synced.id);
16141
+ briefGenerated = Boolean(briefPath);
16142
+ }
16143
+ const nextSource = {
16144
+ ...synced,
16145
+ briefPath: briefPath ?? synced.briefPath,
16146
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
16147
+ };
16148
+ const nextSources = existing ? sources.map((candidate) => candidate.id === nextSource.id ? nextSource : candidate) : [...sources, nextSource];
16149
+ await saveManagedSources(rootDir, nextSources);
16150
+ return {
16151
+ source: nextSource,
16152
+ compile,
16153
+ briefGenerated
16154
+ };
16155
+ }
16156
+ async function reloadManagedSources(rootDir, options = {}) {
16157
+ const compileRequested = options.compile ?? true;
16158
+ const briefRequested = options.brief ?? true;
16159
+ const sources = await loadManagedSources(rootDir);
16160
+ const selected = options.all || !options.id ? sources : sources.filter((source) => source.id === options.id);
16161
+ if (!selected.length) {
16162
+ throw new Error(options.id ? `Managed source not found: ${options.id}` : "No managed sources registered.");
16163
+ }
16164
+ const syncedSources = [];
16165
+ const changedSources = [];
16166
+ for (const source of selected) {
16167
+ const synced = await syncManagedSource(rootDir, source, options);
16168
+ syncedSources.push(synced);
16169
+ if (synced.changed) {
16170
+ changedSources.push(synced);
16171
+ }
16172
+ }
16173
+ const graphExists = await loadVaultConfig(rootDir).then(({ paths }) => fileExists(paths.graphPath));
16174
+ let compile;
16175
+ if (shouldCompile(changedSources, graphExists, compileRequested)) {
16176
+ compile = await compileVault(rootDir, {});
16177
+ }
16178
+ const briefPaths = compileRequested && briefRequested ? await generateBriefsForSources(
16179
+ rootDir,
16180
+ syncedSources.filter((source) => source.status === "ready")
16181
+ ) : /* @__PURE__ */ new Map();
16182
+ const nextSources = sources.map((source) => {
16183
+ const synced = syncedSources.find((candidate) => candidate.id === source.id);
16184
+ if (!synced) {
16185
+ return source;
16186
+ }
16187
+ return {
16188
+ ...synced,
16189
+ briefPath: briefPaths.get(synced.id) ?? synced.briefPath,
16190
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
16191
+ };
16192
+ });
16193
+ await saveManagedSources(rootDir, nextSources);
16194
+ return {
16195
+ sources: nextSources.filter((source) => selected.some((candidate) => candidate.id === source.id)),
16196
+ compile,
16197
+ briefPaths: [...briefPaths.values()]
16198
+ };
16199
+ }
16200
+ async function deleteManagedSource(rootDir, id) {
16201
+ const sources = await loadManagedSources(rootDir);
16202
+ const target = sources.find((source) => source.id === id);
16203
+ if (!target) {
16204
+ throw new Error(`Managed source not found: ${id}`);
16205
+ }
16206
+ await saveManagedSources(
16207
+ rootDir,
16208
+ sources.filter((source) => source.id !== id)
16209
+ );
16210
+ const workingDir = await managedSourceWorkingDir(rootDir, id);
16211
+ await fs21.rm(workingDir, { recursive: true, force: true });
16212
+ return { removed: target };
16213
+ }
16214
+
15114
16215
  // src/viewer.ts
15115
16216
  import { execFile } from "child_process";
15116
- import fs20 from "fs/promises";
16217
+ import fs22 from "fs/promises";
15117
16218
  import http from "http";
15118
- import path25 from "path";
16219
+ import path27 from "path";
15119
16220
  import { promisify } from "util";
15120
16221
  import matter10 from "gray-matter";
15121
16222
  import mime2 from "mime-types";
15122
16223
 
15123
16224
  // src/watch.ts
15124
- import path24 from "path";
16225
+ import path26 from "path";
15125
16226
  import process3 from "process";
15126
16227
  import chokidar from "chokidar";
15127
16228
  var MAX_BACKOFF_MS = 3e4;
15128
16229
  var BACKOFF_THRESHOLD = 3;
15129
16230
  var CRITICAL_THRESHOLD = 10;
15130
16231
  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);
16232
+ function withinRoot3(rootPath, targetPath) {
16233
+ const relative = path26.relative(rootPath, targetPath);
16234
+ return relative === "" || !relative.startsWith("..") && !path26.isAbsolute(relative);
15134
16235
  }
15135
16236
  function hasIgnoredRepoSegment(baseDir, targetPath) {
15136
- const relativePath = path24.relative(baseDir, targetPath);
16237
+ const relativePath = path26.relative(baseDir, targetPath);
15137
16238
  if (!relativePath || relativePath.startsWith("..")) {
15138
16239
  return false;
15139
16240
  }
15140
- return relativePath.split(path24.sep).some((segment) => REPO_WATCH_IGNORES.has(segment));
16241
+ return relativePath.split(path26.sep).some((segment) => REPO_WATCH_IGNORES.has(segment));
15141
16242
  }
15142
16243
  function workspaceIgnoreRoots(rootDir, paths) {
15143
16244
  return [
@@ -15146,16 +16247,16 @@ function workspaceIgnoreRoots(rootDir, paths) {
15146
16247
  paths.stateDir,
15147
16248
  paths.agentDir,
15148
16249
  paths.inboxDir,
15149
- path24.join(rootDir, ".claude"),
15150
- path24.join(rootDir, ".cursor"),
15151
- path24.join(rootDir, ".obsidian")
15152
- ].map((candidate) => path24.resolve(candidate));
16250
+ path26.join(rootDir, ".claude"),
16251
+ path26.join(rootDir, ".cursor"),
16252
+ path26.join(rootDir, ".obsidian")
16253
+ ].map((candidate) => path26.resolve(candidate));
15153
16254
  }
15154
16255
  async function resolveWatchTargets(rootDir, paths, options) {
15155
- const targets = /* @__PURE__ */ new Set([path24.resolve(paths.inboxDir)]);
16256
+ const targets = /* @__PURE__ */ new Set([path26.resolve(paths.inboxDir)]);
15156
16257
  if (options.repo) {
15157
16258
  for (const repoRoot of await listTrackedRepoRoots(rootDir)) {
15158
- targets.add(path24.resolve(repoRoot));
16259
+ targets.add(path26.resolve(repoRoot));
15159
16260
  }
15160
16261
  }
15161
16262
  return [...targets].sort((left, right) => left.localeCompare(right));
@@ -15285,7 +16386,7 @@ async function watchVault(rootDir, options = {}) {
15285
16386
  const { paths } = await initWorkspace(rootDir);
15286
16387
  const baseDebounceMs = options.debounceMs ?? 900;
15287
16388
  const ignoredRoots = workspaceIgnoreRoots(rootDir, paths);
15288
- const inboxWatchRoot = path24.resolve(paths.inboxDir);
16389
+ const inboxWatchRoot = path26.resolve(paths.inboxDir);
15289
16390
  let watchTargets = await resolveWatchTargets(rootDir, paths, options);
15290
16391
  let timer;
15291
16392
  let running = false;
@@ -15300,12 +16401,12 @@ async function watchVault(rootDir, options = {}) {
15300
16401
  usePolling: true,
15301
16402
  interval: 100,
15302
16403
  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;
16404
+ const absolutePath = path26.resolve(targetPath);
16405
+ const primaryTarget = watchTargets.filter((watchTarget) => withinRoot3(watchTarget, absolutePath)).sort((left, right) => right.length - left.length)[0] ?? null;
15305
16406
  if (!primaryTarget) {
15306
16407
  return false;
15307
16408
  }
15308
- if (primaryTarget !== inboxWatchRoot && ignoredRoots.some((ignoreRoot) => withinRoot2(ignoreRoot, absolutePath))) {
16409
+ if (primaryTarget !== inboxWatchRoot && ignoredRoots.some((ignoreRoot) => withinRoot3(ignoreRoot, absolutePath))) {
15309
16410
  return true;
15310
16411
  }
15311
16412
  return hasIgnoredRepoSegment(primaryTarget, absolutePath);
@@ -15489,8 +16590,8 @@ async function watchVault(rootDir, options = {}) {
15489
16590
  }
15490
16591
  };
15491
16592
  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) || ".";
16593
+ const baseDir = watchTargets.filter((watchTarget) => withinRoot3(watchTarget, path26.resolve(targetPath))).sort((left, right) => right.length - left.length)[0] ?? paths.inboxDir;
16594
+ return path26.relative(baseDir, targetPath) || ".";
15494
16595
  };
15495
16596
  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
16597
  await new Promise((resolve, reject) => {
@@ -15532,15 +16633,15 @@ async function getWatchStatus(rootDir) {
15532
16633
  var execFileAsync = promisify(execFile);
15533
16634
  async function readViewerPage(rootDir, relativePath) {
15534
16635
  const { paths } = await loadVaultConfig(rootDir);
15535
- const absolutePath = path25.resolve(paths.wikiDir, relativePath);
16636
+ const absolutePath = path27.resolve(paths.wikiDir, relativePath);
15536
16637
  if (!absolutePath.startsWith(paths.wikiDir) || !await fileExists(absolutePath)) {
15537
16638
  return null;
15538
16639
  }
15539
- const raw = await fs20.readFile(absolutePath, "utf8");
16640
+ const raw = await fs22.readFile(absolutePath, "utf8");
15540
16641
  const parsed = matter10(raw);
15541
16642
  return {
15542
16643
  path: relativePath,
15543
- title: typeof parsed.data.title === "string" ? parsed.data.title : path25.basename(relativePath, path25.extname(relativePath)),
16644
+ title: typeof parsed.data.title === "string" ? parsed.data.title : path27.basename(relativePath, path27.extname(relativePath)),
15544
16645
  frontmatter: parsed.data,
15545
16646
  content: parsed.content,
15546
16647
  assets: normalizeOutputAssets(parsed.data.output_assets)
@@ -15548,12 +16649,12 @@ async function readViewerPage(rootDir, relativePath) {
15548
16649
  }
15549
16650
  async function readViewerAsset(rootDir, relativePath) {
15550
16651
  const { paths } = await loadVaultConfig(rootDir);
15551
- const absolutePath = path25.resolve(paths.wikiDir, relativePath);
16652
+ const absolutePath = path27.resolve(paths.wikiDir, relativePath);
15552
16653
  if (!absolutePath.startsWith(paths.wikiDir) || !await fileExists(absolutePath)) {
15553
16654
  return null;
15554
16655
  }
15555
16656
  return {
15556
- buffer: await fs20.readFile(absolutePath),
16657
+ buffer: await fs22.readFile(absolutePath),
15557
16658
  mimeType: mime2.lookup(absolutePath) || "application/octet-stream"
15558
16659
  };
15559
16660
  }
@@ -15576,12 +16677,12 @@ async function readJsonBody(request) {
15576
16677
  return JSON.parse(raw);
15577
16678
  }
15578
16679
  async function ensureViewerDist(viewerDistDir) {
15579
- const indexPath = path25.join(viewerDistDir, "index.html");
16680
+ const indexPath = path27.join(viewerDistDir, "index.html");
15580
16681
  if (await fileExists(indexPath)) {
15581
16682
  return;
15582
16683
  }
15583
- const viewerProjectDir = path25.dirname(viewerDistDir);
15584
- if (await fileExists(path25.join(viewerProjectDir, "package.json"))) {
16684
+ const viewerProjectDir = path27.dirname(viewerDistDir);
16685
+ if (await fileExists(path27.join(viewerProjectDir, "package.json"))) {
15585
16686
  await execFileAsync("pnpm", ["build"], { cwd: viewerProjectDir });
15586
16687
  }
15587
16688
  }
@@ -15598,7 +16699,7 @@ async function startGraphServer(rootDir, port) {
15598
16699
  return;
15599
16700
  }
15600
16701
  response.writeHead(200, { "content-type": "application/json" });
15601
- response.end(await fs20.readFile(paths.graphPath, "utf8"));
16702
+ response.end(await fs22.readFile(paths.graphPath, "utf8"));
15602
16703
  return;
15603
16704
  }
15604
16705
  if (url.pathname === "/api/graph/query") {
@@ -15655,14 +16756,14 @@ async function startGraphServer(rootDir, port) {
15655
16756
  return;
15656
16757
  }
15657
16758
  if (url.pathname === "/api/graph-report") {
15658
- const reportPath = path25.join(paths.wikiDir, "graph", "report.json");
16759
+ const reportPath = path27.join(paths.wikiDir, "graph", "report.json");
15659
16760
  if (!await fileExists(reportPath)) {
15660
16761
  response.writeHead(404, { "content-type": "application/json" });
15661
16762
  response.end(JSON.stringify({ error: "Graph report artifact not found. Run `swarmvault compile` first." }));
15662
16763
  return;
15663
16764
  }
15664
16765
  response.writeHead(200, { "content-type": "application/json" });
15665
- response.end(await fs20.readFile(reportPath, "utf8"));
16766
+ response.end(await fs22.readFile(reportPath, "utf8"));
15666
16767
  return;
15667
16768
  }
15668
16769
  if (url.pathname === "/api/watch-status") {
@@ -15745,8 +16846,8 @@ async function startGraphServer(rootDir, port) {
15745
16846
  return;
15746
16847
  }
15747
16848
  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");
16849
+ const target = path27.join(paths.viewerDistDir, relativePath);
16850
+ const fallback = path27.join(paths.viewerDistDir, "index.html");
15750
16851
  const filePath = await fileExists(target) ? target : fallback;
15751
16852
  if (!await fileExists(filePath)) {
15752
16853
  response.writeHead(503, { "content-type": "text/plain" });
@@ -15754,7 +16855,7 @@ async function startGraphServer(rootDir, port) {
15754
16855
  return;
15755
16856
  }
15756
16857
  response.writeHead(200, { "content-type": mime2.lookup(filePath) || "text/plain" });
15757
- response.end(await fs20.readFile(filePath));
16858
+ response.end(await fs22.readFile(filePath));
15758
16859
  });
15759
16860
  await new Promise((resolve) => {
15760
16861
  server.listen(effectivePort, resolve);
@@ -15781,7 +16882,7 @@ async function exportGraphHtml(rootDir, outputPath) {
15781
16882
  throw new Error("Graph artifact not found. Run `swarmvault compile` first.");
15782
16883
  }
15783
16884
  await ensureViewerDist(paths.viewerDistDir);
15784
- const indexPath = path25.join(paths.viewerDistDir, "index.html");
16885
+ const indexPath = path27.join(paths.viewerDistDir, "index.html");
15785
16886
  if (!await fileExists(indexPath)) {
15786
16887
  throw new Error("Viewer build not found. Run `pnpm build` first.");
15787
16888
  }
@@ -15807,17 +16908,17 @@ async function exportGraphHtml(rootDir, outputPath) {
15807
16908
  } : null;
15808
16909
  })
15809
16910
  );
15810
- const rawHtml = await fs20.readFile(indexPath, "utf8");
16911
+ const rawHtml = await fs22.readFile(indexPath, "utf8");
15811
16912
  const scriptMatch = rawHtml.match(/<script type="module" crossorigin src="([^"]+)"><\/script>/);
15812
16913
  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;
16914
+ const scriptPath = scriptMatch?.[1] ? path27.join(paths.viewerDistDir, scriptMatch[1].replace(/^\//, "")) : null;
16915
+ const stylePath = styleMatch?.[1] ? path27.join(paths.viewerDistDir, styleMatch[1].replace(/^\//, "")) : null;
15815
16916
  if (!scriptPath || !await fileExists(scriptPath)) {
15816
16917
  throw new Error("Viewer script bundle not found. Run `pnpm build` first.");
15817
16918
  }
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"));
16919
+ const script = await fs22.readFile(scriptPath, "utf8");
16920
+ const style = stylePath && await fileExists(stylePath) ? await fs22.readFile(stylePath, "utf8") : "";
16921
+ const report = await readJsonFile(path27.join(paths.wikiDir, "graph", "report.json"));
15821
16922
  const embeddedData = JSON.stringify({ graph, pages: pages.filter(Boolean), report }, null, 2).replace(/</g, "\\u003c");
15822
16923
  const html = [
15823
16924
  "<!doctype html>",
@@ -15836,13 +16937,14 @@ async function exportGraphHtml(rootDir, outputPath) {
15836
16937
  "</html>",
15837
16938
  ""
15838
16939
  ].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);
16940
+ await fs22.mkdir(path27.dirname(outputPath), { recursive: true });
16941
+ await fs22.writeFile(outputPath, html, "utf8");
16942
+ return path27.resolve(outputPath);
15842
16943
  }
15843
16944
  export {
15844
16945
  acceptApproval,
15845
16946
  addInput,
16947
+ addManagedSource,
15846
16948
  archiveCandidate,
15847
16949
  assertProviderCapability,
15848
16950
  benchmarkVault,
@@ -15853,6 +16955,7 @@ export {
15853
16955
  createWebSearchAdapter,
15854
16956
  defaultVaultConfig,
15855
16957
  defaultVaultSchema,
16958
+ deleteManagedSource,
15856
16959
  explainGraphVault,
15857
16960
  exploreVault,
15858
16961
  exportGraphFormat,
@@ -15875,6 +16978,7 @@ export {
15875
16978
  listCandidates,
15876
16979
  listGodNodes,
15877
16980
  listGraphHyperedges,
16981
+ listManagedSourceRecords,
15878
16982
  listManifests,
15879
16983
  listPages,
15880
16984
  listSchedules,
@@ -15892,6 +16996,7 @@ export {
15892
16996
  readGraphReport,
15893
16997
  readPage,
15894
16998
  rejectApproval,
16999
+ reloadManagedSources,
15895
17000
  resolvePaths,
15896
17001
  runSchedule,
15897
17002
  runWatchCycle,