@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/LICENSE +21 -0
- package/README.md +20 -1
- package/dist/chunk-CWLDFLH2.js +1163 -0
- package/dist/index.d.ts +62 -2
- package/dist/index.js +1561 -456
- package/dist/registry-2REAPKPO.js +12 -0
- package/package.json +7 -7
package/dist/index.js
CHANGED
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
uniqueBy,
|
|
22
22
|
writeFileIfChanged,
|
|
23
23
|
writeJsonFile
|
|
24
|
-
} from "./chunk-
|
|
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
|
|
1725
|
-
import
|
|
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(/^["
|
|
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/
|
|
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(
|
|
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 =
|
|
5188
|
+
const absolutePath = path11.join(paths.wikiDir, page.path);
|
|
4838
5189
|
if (!await fileExists(absolutePath)) {
|
|
4839
5190
|
continue;
|
|
4840
5191
|
}
|
|
4841
|
-
const raw = await
|
|
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 =
|
|
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 ?
|
|
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) =>
|
|
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 =
|
|
5412
|
+
let current = path12.resolve(startPath);
|
|
5062
5413
|
try {
|
|
5063
|
-
const stat = await
|
|
5414
|
+
const stat = await fs11.stat(current);
|
|
5064
5415
|
if (!stat.isDirectory()) {
|
|
5065
|
-
current =
|
|
5416
|
+
current = path12.dirname(current);
|
|
5066
5417
|
}
|
|
5067
5418
|
} catch {
|
|
5068
|
-
current =
|
|
5419
|
+
current = path12.dirname(current);
|
|
5069
5420
|
}
|
|
5070
5421
|
while (true) {
|
|
5071
|
-
if (await fileExists(
|
|
5422
|
+
if (await fileExists(path12.join(current, ".git"))) {
|
|
5072
5423
|
return current;
|
|
5073
5424
|
}
|
|
5074
|
-
const parent =
|
|
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 =
|
|
5083
|
-
return relative === "" || !relative.startsWith("..") && !
|
|
5433
|
+
const relative = path12.relative(rootPath, targetPath);
|
|
5434
|
+
return relative === "" || !relative.startsWith("..") && !path12.isAbsolute(relative);
|
|
5084
5435
|
}
|
|
5085
|
-
function
|
|
5436
|
+
function repoRootFromManifest2(manifest) {
|
|
5086
5437
|
if (manifest.originType !== "file" || !manifest.originalPath || !manifest.repoRelativePath) {
|
|
5087
5438
|
return null;
|
|
5088
5439
|
}
|
|
5089
|
-
const repoDir =
|
|
5090
|
-
const fileDir =
|
|
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
|
|
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(
|
|
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 =
|
|
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("#") ||
|
|
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
|
|
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(
|
|
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
|
|
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(
|
|
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 =
|
|
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
|
|
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
|
|
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 =
|
|
5593
|
-
const relativeToRepo = repoRelativePathFor(absolutePath, repoRoot) ?? toPosix(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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 =
|
|
5655
|
-
const directory =
|
|
5656
|
-
const basename = extension ?
|
|
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 :
|
|
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 =
|
|
5813
|
-
const extractedTextPath = prepared.extractedText ?
|
|
5814
|
-
const extractedMetadataPath = prepared.extractionArtifact ?
|
|
5815
|
-
const attachmentsDir =
|
|
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
|
|
6168
|
+
await fs11.rm(path12.resolve(rootDir, previous.storedPath), { force: true });
|
|
5818
6169
|
}
|
|
5819
6170
|
if (previous?.extractedTextPath) {
|
|
5820
|
-
await
|
|
6171
|
+
await fs11.rm(path12.resolve(rootDir, previous.extractedTextPath), { force: true });
|
|
5821
6172
|
}
|
|
5822
6173
|
if (previous?.extractedMetadataPath) {
|
|
5823
|
-
await
|
|
6174
|
+
await fs11.rm(path12.resolve(rootDir, previous.extractedMetadataPath), { force: true });
|
|
5824
6175
|
}
|
|
5825
|
-
await
|
|
5826
|
-
await
|
|
6176
|
+
await fs11.rm(attachmentsDir, { recursive: true, force: true });
|
|
6177
|
+
await fs11.writeFile(storedPath, prepared.payloadBytes);
|
|
5827
6178
|
if (prepared.extractedText && extractedTextPath) {
|
|
5828
|
-
await
|
|
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 =
|
|
5836
|
-
await ensureDir(
|
|
5837
|
-
await
|
|
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(
|
|
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(
|
|
5856
|
-
extractedTextPath: extractedTextPath ? toPosix(
|
|
5857
|
-
extractedMetadataPath: extractedMetadataPath ? toPosix(
|
|
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(
|
|
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
|
|
5884
|
-
await
|
|
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
|
|
6237
|
+
await fs11.rm(path12.resolve(rootDir, manifest.extractedTextPath), { force: true });
|
|
5887
6238
|
}
|
|
5888
6239
|
if (manifest.extractedMetadataPath) {
|
|
5889
|
-
await
|
|
6240
|
+
await fs11.rm(path12.resolve(rootDir, manifest.extractedMetadataPath), { force: true });
|
|
5890
6241
|
}
|
|
5891
|
-
await
|
|
5892
|
-
await
|
|
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
|
-
|
|
5902
|
-
|
|
5903
|
-
|
|
6252
|
+
path12.join(rootDir, ".claude"),
|
|
6253
|
+
path12.join(rootDir, ".cursor"),
|
|
6254
|
+
path12.join(rootDir, ".obsidian")
|
|
5904
6255
|
];
|
|
5905
|
-
return candidates.map((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) =>
|
|
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) =>
|
|
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 =
|
|
5933
|
-
if (!repoRoot || !uniqueRoots.includes(
|
|
6290
|
+
const repoRoot = repoRootFromManifest2(manifest);
|
|
6291
|
+
if (!repoRoot || !uniqueRoots.includes(path12.resolve(repoRoot))) {
|
|
5934
6292
|
continue;
|
|
5935
6293
|
}
|
|
5936
|
-
const key =
|
|
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(
|
|
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) =>
|
|
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(
|
|
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(
|
|
6337
|
+
progress.finish(`repo=${toPosix(path12.relative(rootDir, repoRoot)) || "."}`);
|
|
5980
6338
|
for (const manifest of repoManifests) {
|
|
5981
|
-
const originalPath = manifest.originalPath ?
|
|
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(
|
|
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) =>
|
|
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 =
|
|
6018
|
-
if (!repoRoot || !uniqueRoots.includes(
|
|
6375
|
+
const repoRoot = repoRootFromManifest2(manifest);
|
|
6376
|
+
if (!repoRoot || !uniqueRoots.includes(path12.resolve(repoRoot))) {
|
|
6019
6377
|
continue;
|
|
6020
6378
|
}
|
|
6021
|
-
const key =
|
|
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) => [
|
|
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(
|
|
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(
|
|
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) =>
|
|
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(
|
|
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(
|
|
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(
|
|
6441
|
+
prepared.repoRelativePath ?? toPosix(path12.relative(repoRoot, absolutePath))
|
|
6084
6442
|
),
|
|
6085
6443
|
repoRoot,
|
|
6086
|
-
path: toPosix(
|
|
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(
|
|
6465
|
+
progress.finish(`repo=${toPosix(path12.relative(rootDir, repoRoot)) || "."}`);
|
|
6108
6466
|
for (const manifest of repoManifests) {
|
|
6109
|
-
const originalPath = manifest.originalPath ?
|
|
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(
|
|
6471
|
+
id: pendingSemanticRefreshId("removed", repoRoot, manifest.repoRelativePath ?? toPosix(path12.relative(repoRoot, originalPath))),
|
|
6114
6472
|
repoRoot,
|
|
6115
|
-
path: toPosix(
|
|
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(
|
|
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
|
|
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 =
|
|
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(
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
|
6746
|
+
const originalBytes = await fs11.readFile(absolutePath);
|
|
6389
6747
|
const originalText = originalBytes.toString("utf8");
|
|
6390
|
-
const title = titleFromText(
|
|
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
|
|
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:
|
|
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
|
|
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
|
|
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 =
|
|
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:
|
|
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
|
|
6829
|
+
async function ingestInputDetailed(rootDir, input, options) {
|
|
6472
6830
|
const { paths } = await initWorkspace(rootDir);
|
|
6473
6831
|
const normalizedOptions = normalizeIngestOptions(options);
|
|
6474
|
-
const absoluteInput =
|
|
6475
|
-
const repoRoot = isHttpUrl(input) || normalizedOptions.repoRoot ? normalizedOptions.repoRoot : await findNearestGitRoot2(absoluteInput).then((value) => value ??
|
|
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
|
-
|
|
6478
|
-
|
|
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 =
|
|
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(
|
|
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(
|
|
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(
|
|
6589
|
-
`repo_root=${toPosix(
|
|
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 =
|
|
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 =
|
|
6977
|
+
const basename = path12.basename(absolutePath);
|
|
6618
6978
|
if (basename.startsWith(".")) {
|
|
6619
|
-
skipped.push({ path: toPosix(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
|
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(
|
|
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 =
|
|
7039
|
+
const absolutePath = path12.resolve(rootDir, manifest.extractedTextPath);
|
|
6671
7040
|
if (!await fileExists(absolutePath)) {
|
|
6672
7041
|
return void 0;
|
|
6673
7042
|
}
|
|
6674
|
-
return
|
|
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 =
|
|
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
|
|
6689
|
-
import
|
|
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
|
|
6696
|
-
import
|
|
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
|
|
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
|
|
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(
|
|
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
|
|
6803
|
-
import
|
|
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
|
|
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 =
|
|
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
|
|
7128
|
-
import
|
|
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
|
|
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 ?
|
|
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
|
|
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 =
|
|
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 =
|
|
7502
|
-
const raw = await
|
|
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 ${
|
|
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
|
|
7730
|
-
import
|
|
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 =
|
|
7765
|
-
const content = await
|
|
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
|
|
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 =
|
|
9638
|
+
const pathEdges = path28.edgeIds.map((edgeId) => edgesById.get(edgeId)).filter((item) => Boolean(item));
|
|
9270
9639
|
return {
|
|
9271
|
-
pathNodeIds:
|
|
9272
|
-
pathEdgeIds:
|
|
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:
|
|
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
|
|
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:
|
|
9356
|
-
pathEdgeIds:
|
|
9357
|
-
pathRelations:
|
|
9358
|
-
pathEvidenceClasses:
|
|
9359
|
-
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
|
|
10287
|
-
import
|
|
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
|
|
10292
|
-
import
|
|
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
|
|
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 :
|
|
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 =
|
|
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) =>
|
|
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(
|
|
10458
|
-
const content = await
|
|
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
|
|
10461
|
-
const title = typeof parsed.data.title === "string" ? parsed.data.title :
|
|
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 =
|
|
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(
|
|
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 =
|
|
10536
|
-
const entries = await
|
|
10904
|
+
const outputsDir = path20.join(wikiDir, "outputs");
|
|
10905
|
+
const entries = await fs16.readdir(outputsDir, { withFileTypes: true }).catch(() => []);
|
|
10537
10906
|
const outputs = [];
|
|
10538
|
-
|
|
10539
|
-
|
|
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
|
|
10543
|
-
const
|
|
10544
|
-
|
|
10545
|
-
|
|
10546
|
-
|
|
10547
|
-
|
|
10548
|
-
|
|
10549
|
-
|
|
10550
|
-
|
|
10551
|
-
|
|
10552
|
-
|
|
10553
|
-
|
|
10554
|
-
|
|
10555
|
-
|
|
10556
|
-
|
|
10557
|
-
|
|
10558
|
-
|
|
10559
|
-
|
|
10560
|
-
|
|
10561
|
-
|
|
10562
|
-
|
|
10563
|
-
|
|
10564
|
-
|
|
10565
|
-
|
|
10566
|
-
|
|
10567
|
-
|
|
10568
|
-
|
|
10569
|
-
|
|
10570
|
-
|
|
10571
|
-
|
|
10572
|
-
|
|
10573
|
-
|
|
10574
|
-
|
|
10575
|
-
|
|
10576
|
-
|
|
10577
|
-
|
|
10578
|
-
|
|
10579
|
-
|
|
10580
|
-
|
|
10581
|
-
|
|
10582
|
-
|
|
10583
|
-
|
|
10584
|
-
|
|
10585
|
-
|
|
10586
|
-
|
|
10587
|
-
|
|
10588
|
-
|
|
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
|
|
10596
|
-
import
|
|
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(
|
|
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 =
|
|
10655
|
-
const content = await
|
|
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(
|
|
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-
|
|
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(
|
|
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 (!
|
|
11552
|
+
if (!path22.isAbsolute(rawPath)) {
|
|
11169
11553
|
return normalizeProjectRoot(rawPath);
|
|
11170
11554
|
}
|
|
11171
|
-
const relative = toPosix(
|
|
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
|
|
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
|
|
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 ??
|
|
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}:${
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
12099
|
-
|
|
12100
|
-
|
|
12101
|
-
|
|
12102
|
-
|
|
12103
|
-
|
|
12104
|
-
|
|
12105
|
-
|
|
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(
|
|
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
|
|
12501
|
+
return path22.join(paths.approvalsDir, approvalId, "manifest.json");
|
|
12118
12502
|
}
|
|
12119
12503
|
function approvalGraphPath(paths, approvalId) {
|
|
12120
|
-
return
|
|
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
|
|
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(
|
|
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 ??
|
|
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 =
|
|
12574
|
+
const approvalDir = path22.join(paths.approvalsDir, approvalId);
|
|
12191
12575
|
await ensureDir(approvalDir);
|
|
12192
|
-
await ensureDir(
|
|
12193
|
-
await ensureDir(
|
|
12576
|
+
await ensureDir(path22.join(approvalDir, "wiki"));
|
|
12577
|
+
await ensureDir(path22.join(approvalDir, "state"));
|
|
12194
12578
|
for (const file of changedFiles) {
|
|
12195
|
-
const targetPath =
|
|
12196
|
-
await ensureDir(
|
|
12197
|
-
await
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
12342
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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 =
|
|
12576
|
-
const current = await fileExists(absolutePath) ? await
|
|
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
|
|
12985
|
+
await fs18.rm(path22.join(paths.wikiDir, relativePath), { force: true });
|
|
12602
12986
|
}
|
|
12603
12987
|
await writeJsonFile(paths.graphPath, graph);
|
|
12604
|
-
await writeJsonFile(
|
|
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(
|
|
12676
|
-
ensureDir(
|
|
12677
|
-
ensureDir(
|
|
12678
|
-
ensureDir(
|
|
12679
|
-
ensureDir(
|
|
12680
|
-
ensureDir(
|
|
12681
|
-
ensureDir(
|
|
12682
|
-
ensureDir(
|
|
12683
|
-
ensureDir(
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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(
|
|
13143
|
+
await writeFileIfChanged(path22.join(paths.wikiDir, record.page.path), record.content);
|
|
12760
13144
|
}
|
|
12761
13145
|
if (graphOrientation.report) {
|
|
12762
|
-
await writeJsonFile(
|
|
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(
|
|
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) =>
|
|
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(
|
|
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) =>
|
|
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 =
|
|
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(
|
|
12808
|
-
await
|
|
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 =
|
|
12811
|
-
await ensureDir(
|
|
13194
|
+
const assetPath = path22.join(paths.wikiDir, assetFile.relativePath);
|
|
13195
|
+
await ensureDir(path22.dirname(assetPath));
|
|
12812
13196
|
if (typeof assetFile.content === "string") {
|
|
12813
|
-
await
|
|
13197
|
+
await fs18.writeFile(assetPath, assetFile.content, assetFile.encoding ?? "utf8");
|
|
12814
13198
|
} else {
|
|
12815
|
-
await
|
|
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 =
|
|
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(
|
|
12849
|
-
await
|
|
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 =
|
|
12852
|
-
await ensureDir(
|
|
13235
|
+
const assetPath = path22.join(paths.wikiDir, assetFile.relativePath);
|
|
13236
|
+
await ensureDir(path22.dirname(assetPath));
|
|
12853
13237
|
if (typeof assetFile.content === "string") {
|
|
12854
|
-
await
|
|
13238
|
+
await fs18.writeFile(assetPath, assetFile.content, assetFile.encoding ?? "utf8");
|
|
12855
13239
|
} else {
|
|
12856
|
-
await
|
|
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 =
|
|
13257
|
+
const approvalDir = path22.join(paths.approvalsDir, approvalId);
|
|
12874
13258
|
await ensureDir(approvalDir);
|
|
12875
|
-
await ensureDir(
|
|
12876
|
-
await ensureDir(
|
|
13259
|
+
await ensureDir(path22.join(approvalDir, "wiki"));
|
|
13260
|
+
await ensureDir(path22.join(approvalDir, "state"));
|
|
12877
13261
|
for (const file of changedFiles) {
|
|
12878
|
-
const targetPath =
|
|
12879
|
-
await ensureDir(
|
|
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
|
|
13265
|
+
await fs18.writeFile(targetPath, Buffer.from(file.content, "base64"));
|
|
12882
13266
|
} else {
|
|
12883
|
-
await
|
|
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
|
|
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 =
|
|
13311
|
+
const absolutePath = path22.join(paths.wikiDir, result.path);
|
|
12928
13312
|
try {
|
|
12929
|
-
const content = await
|
|
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
|
|
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
|
|
13180
|
-
const stagedContent = entry.nextPath ? await
|
|
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 =
|
|
13214
|
-
const stagedContent = await
|
|
13215
|
-
const targetAbsolutePath =
|
|
13216
|
-
await ensureDir(
|
|
13217
|
-
await
|
|
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
|
|
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 =
|
|
13224
|
-
await
|
|
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 =
|
|
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 =
|
|
13231
|
-
await ensureDir(
|
|
13232
|
-
await
|
|
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
|
|
13627
|
+
await fs18.rm(path22.join(paths.wikiDir, entry.previousPath), { force: true });
|
|
13244
13628
|
}
|
|
13245
13629
|
if (deletedPage?.kind === "output") {
|
|
13246
|
-
await
|
|
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
|
|
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 =
|
|
13350
|
-
await ensureDir(
|
|
13351
|
-
await
|
|
13352
|
-
await
|
|
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
|
|
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 =
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
13716
|
-
ensureDir(
|
|
13717
|
-
ensureDir(
|
|
13718
|
-
ensureDir(
|
|
13719
|
-
ensureDir(
|
|
13720
|
-
ensureDir(
|
|
13721
|
-
ensureDir(
|
|
13722
|
-
ensureDir(
|
|
13723
|
-
ensureDir(
|
|
13724
|
-
ensureDir(
|
|
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 =
|
|
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 =
|
|
14511
|
+
result.stagedPath = path22.join(approval.approvalDir, "wiki", stagedStepPages[index]?.page.path ?? "");
|
|
14128
14512
|
});
|
|
14129
|
-
stagedHubPath =
|
|
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 =
|
|
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
|
|
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(
|
|
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 =
|
|
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
|
|
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 :
|
|
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:
|
|
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:
|
|
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:
|
|
14728
|
+
pagePath: path22.join(paths.wikiDir, page.path),
|
|
14345
14729
|
relatedPageIds: [page.id]
|
|
14346
14730
|
});
|
|
14347
14731
|
}
|
|
14348
|
-
const absolutePath =
|
|
14732
|
+
const absolutePath = path22.join(paths.wikiDir, page.path);
|
|
14349
14733
|
if (await fileExists(absolutePath)) {
|
|
14350
|
-
const content = await
|
|
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
|
|
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(
|
|
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 =
|
|
14772
|
-
return asTextResource(`swarmvault://pages/${encodedPath}`, await
|
|
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(
|
|
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:
|
|
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 =
|
|
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
|
|
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
|
|
14854
|
-
import
|
|
15237
|
+
import fs20 from "fs/promises";
|
|
15238
|
+
import path24 from "path";
|
|
14855
15239
|
function scheduleStatePath(schedulesDir, jobId) {
|
|
14856
|
-
return
|
|
15240
|
+
return path24.join(schedulesDir, `${encodeURIComponent(jobId)}.json`);
|
|
14857
15241
|
}
|
|
14858
15242
|
function scheduleLockPath(schedulesDir, jobId) {
|
|
14859
|
-
return
|
|
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
|
|
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
|
|
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
|
|
16217
|
+
import fs22 from "fs/promises";
|
|
15117
16218
|
import http from "http";
|
|
15118
|
-
import
|
|
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
|
|
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
|
|
15132
|
-
const relative =
|
|
15133
|
-
return relative === "" || !relative.startsWith("..") && !
|
|
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 =
|
|
16237
|
+
const relativePath = path26.relative(baseDir, targetPath);
|
|
15137
16238
|
if (!relativePath || relativePath.startsWith("..")) {
|
|
15138
16239
|
return false;
|
|
15139
16240
|
}
|
|
15140
|
-
return relativePath.split(
|
|
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
|
-
|
|
15150
|
-
|
|
15151
|
-
|
|
15152
|
-
].map((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([
|
|
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(
|
|
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 =
|
|
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 =
|
|
15304
|
-
const primaryTarget = watchTargets.filter((watchTarget) =>
|
|
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) =>
|
|
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) =>
|
|
15493
|
-
return
|
|
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 =
|
|
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
|
|
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 :
|
|
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 =
|
|
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
|
|
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 =
|
|
16680
|
+
const indexPath = path27.join(viewerDistDir, "index.html");
|
|
15580
16681
|
if (await fileExists(indexPath)) {
|
|
15581
16682
|
return;
|
|
15582
16683
|
}
|
|
15583
|
-
const viewerProjectDir =
|
|
15584
|
-
if (await fileExists(
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
15749
|
-
const fallback =
|
|
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
|
|
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 =
|
|
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
|
|
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] ?
|
|
15814
|
-
const stylePath = styleMatch?.[1] ?
|
|
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
|
|
15819
|
-
const style = stylePath && await fileExists(stylePath) ? await
|
|
15820
|
-
const report = await readJsonFile(
|
|
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
|
|
15840
|
-
await
|
|
15841
|
-
return
|
|
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,
|