@swarmvaultai/engine 0.6.5 → 0.6.7
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/dist/chunk-HRRPWXRZ.js +1335 -0
- package/dist/index.js +436 -194
- package/dist/registry-NBLIJHZT.js +12 -0
- package/package.json +9 -8
package/dist/index.js
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
firstSentences,
|
|
10
10
|
getProviderForTask,
|
|
11
11
|
initWorkspace,
|
|
12
|
+
isPathWithin,
|
|
12
13
|
listFilesRecursive,
|
|
13
14
|
loadVaultConfig,
|
|
14
15
|
normalizeWhitespace,
|
|
@@ -21,7 +22,7 @@ import {
|
|
|
21
22
|
uniqueBy,
|
|
22
23
|
writeFileIfChanged,
|
|
23
24
|
writeJsonFile
|
|
24
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-HRRPWXRZ.js";
|
|
25
26
|
|
|
26
27
|
// src/agents.ts
|
|
27
28
|
import crypto from "crypto";
|
|
@@ -63,6 +64,13 @@ function buildManagedBlock(target) {
|
|
|
63
64
|
""
|
|
64
65
|
].join("\n");
|
|
65
66
|
}
|
|
67
|
+
function buildCursorRule() {
|
|
68
|
+
const frontmatter = YAML.stringify({
|
|
69
|
+
description: "SwarmVault graph-first repository instructions.",
|
|
70
|
+
alwaysApply: true
|
|
71
|
+
}).trimEnd();
|
|
72
|
+
return ["---", frontmatter, "---", "", buildManagedBlock("cursor").trimEnd(), ""].join("\n");
|
|
73
|
+
}
|
|
66
74
|
function supportsAgentHook(agent) {
|
|
67
75
|
return agent === "claude" || agent === "opencode" || agent === "gemini" || agent === "copilot";
|
|
68
76
|
}
|
|
@@ -689,8 +697,7 @@ async function installAgent(rootDir, agent, options = {}) {
|
|
|
689
697
|
await upsertManagedBlock(target, buildManagedBlock("gemini"));
|
|
690
698
|
break;
|
|
691
699
|
case "cursor":
|
|
692
|
-
await writeOwnedFile(target,
|
|
693
|
-
`);
|
|
700
|
+
await writeOwnedFile(target, buildCursorRule());
|
|
694
701
|
break;
|
|
695
702
|
case "aider":
|
|
696
703
|
await upsertManagedBlock(target, buildManagedBlock("aider"));
|
|
@@ -1342,10 +1349,13 @@ function buildBenchmarkArtifact(input) {
|
|
|
1342
1349
|
const corpusTokens = Math.max(1, Math.round(input.corpusWords * (100 / 75)));
|
|
1343
1350
|
const perQuestion = input.perQuestion.filter((entry) => entry.queryTokens > 0).map((entry) => ({
|
|
1344
1351
|
...entry,
|
|
1345
|
-
reduction:
|
|
1352
|
+
// Honest reduction: negative values mean graph context is larger than the
|
|
1353
|
+
// full corpus, which is the truth on very small vaults. Clamping to zero
|
|
1354
|
+
// hid that signal.
|
|
1355
|
+
reduction: Number((1 - entry.queryTokens / Math.max(1, corpusTokens)).toFixed(3))
|
|
1346
1356
|
}));
|
|
1347
1357
|
const avgQueryTokens = perQuestion.length ? Math.max(1, Math.round(perQuestion.reduce((total, entry) => total + entry.queryTokens, 0) / perQuestion.length)) : 0;
|
|
1348
|
-
const reductionRatio = avgQueryTokens ? Number(
|
|
1358
|
+
const reductionRatio = avgQueryTokens ? Number((1 - avgQueryTokens / Math.max(1, corpusTokens)).toFixed(3)) : 0;
|
|
1349
1359
|
const uniqueVisitedNodes = new Set(perQuestion.flatMap((entry) => entry.visitedNodeIds)).size;
|
|
1350
1360
|
const summary = {
|
|
1351
1361
|
questionCount: input.questions.length,
|
|
@@ -1727,6 +1737,7 @@ import { pathToFileURL } from "url";
|
|
|
1727
1737
|
import { Readability } from "@mozilla/readability";
|
|
1728
1738
|
import matter3 from "gray-matter";
|
|
1729
1739
|
import ignore from "ignore";
|
|
1740
|
+
import { isText } from "istextorbinary";
|
|
1730
1741
|
import { JSDOM as JSDOM2 } from "jsdom";
|
|
1731
1742
|
import mime from "mime-types";
|
|
1732
1743
|
import TurndownService2 from "turndown";
|
|
@@ -2446,14 +2457,18 @@ function rubyStringContent(node) {
|
|
|
2446
2457
|
const contentNode = node.descendantsOfType(["string_content", "simple_symbol", "bare_string"]).find((item) => item !== null) ?? null;
|
|
2447
2458
|
return contentNode?.text.trim() || void 0;
|
|
2448
2459
|
}
|
|
2460
|
+
function normalizePowerShellDotSourceSpecifier(raw) {
|
|
2461
|
+
const unquoted = raw.replace(/^['"]+|['"]+$/g, "").trim();
|
|
2462
|
+
return unquoted.replace(/^\$PSScriptRoot(?:[\\/]+|$)/i, "./");
|
|
2463
|
+
}
|
|
2449
2464
|
function parsePowerShellImport(commandNode) {
|
|
2450
2465
|
const commandName = commandNode.descendantsOfType(["command_name", "command_name_expr"]).find((item) => item !== null)?.text.trim();
|
|
2451
2466
|
const genericTokens = commandNode.descendantsOfType("generic_token").filter((item) => item !== null).map((item) => item.text.trim());
|
|
2452
2467
|
if (commandNode.namedChildren.some((child) => child?.type === "command_invokation_operator")) {
|
|
2453
|
-
const
|
|
2454
|
-
if (
|
|
2468
|
+
const raw = commandName?.trim();
|
|
2469
|
+
if (raw) {
|
|
2455
2470
|
return {
|
|
2456
|
-
specifier,
|
|
2471
|
+
specifier: normalizePowerShellDotSourceSpecifier(raw),
|
|
2457
2472
|
importedSymbols: [],
|
|
2458
2473
|
isExternal: false,
|
|
2459
2474
|
reExport: false
|
|
@@ -2934,6 +2949,21 @@ function rustCodeAnalysis(manifest, rootNode, diagnostics) {
|
|
|
2934
2949
|
imports.push(parseRustUse(child.text));
|
|
2935
2950
|
continue;
|
|
2936
2951
|
}
|
|
2952
|
+
if (child.type === "mod_item") {
|
|
2953
|
+
const hasInlineBody = child.namedChildren.some((item) => item?.type === "declaration_list");
|
|
2954
|
+
if (!hasInlineBody) {
|
|
2955
|
+
const modName = extractIdentifier(child.childForFieldName("name") ?? findNamedChild(child, "identifier"));
|
|
2956
|
+
if (modName) {
|
|
2957
|
+
imports.push({
|
|
2958
|
+
specifier: `self::${modName}`,
|
|
2959
|
+
importedSymbols: [],
|
|
2960
|
+
isExternal: false,
|
|
2961
|
+
reExport: false
|
|
2962
|
+
});
|
|
2963
|
+
}
|
|
2964
|
+
}
|
|
2965
|
+
continue;
|
|
2966
|
+
}
|
|
2937
2967
|
const name = child.type === "function_item" ? extractIdentifier(child.childForFieldName("name")) : extractIdentifier(child.childForFieldName("name"));
|
|
2938
2968
|
if (child.type === "impl_item") {
|
|
2939
2969
|
const traitName = normalizeSymbolReference(nodeText(child.childForFieldName("trait")));
|
|
@@ -3517,8 +3547,9 @@ function rubyCodeAnalysis(manifest, rootNode, diagnostics) {
|
|
|
3517
3547
|
if (callee === "require" || callee === "require_relative") {
|
|
3518
3548
|
const specifier = rubyStringContent(child.childForFieldName("arguments") ?? child.namedChildren.at(1) ?? null);
|
|
3519
3549
|
if (specifier) {
|
|
3550
|
+
const normalizedSpecifier = callee === "require_relative" && !specifier.startsWith(".") && !specifier.startsWith("/") ? `./${specifier}` : specifier;
|
|
3520
3551
|
imports.push({
|
|
3521
|
-
specifier,
|
|
3552
|
+
specifier: normalizedSpecifier,
|
|
3522
3553
|
importedSymbols: [],
|
|
3523
3554
|
isExternal: callee === "require" && !specifier.startsWith("."),
|
|
3524
3555
|
reExport: false
|
|
@@ -3765,7 +3796,7 @@ async function analyzeTreeSitterCode(manifest, content, language) {
|
|
|
3765
3796
|
};
|
|
3766
3797
|
}
|
|
3767
3798
|
try {
|
|
3768
|
-
const diagnostics = diagnosticsFromTree(tree.rootNode);
|
|
3799
|
+
const diagnostics = language === "lua" ? [] : diagnosticsFromTree(tree.rootNode);
|
|
3769
3800
|
const rationales = extractTreeSitterRationales(manifest, language, tree.rootNode);
|
|
3770
3801
|
switch (language) {
|
|
3771
3802
|
case "bash":
|
|
@@ -3869,8 +3900,31 @@ function interpreterFromShebang(content) {
|
|
|
3869
3900
|
}
|
|
3870
3901
|
return basename(parts[0] ?? "");
|
|
3871
3902
|
}
|
|
3872
|
-
function
|
|
3873
|
-
|
|
3903
|
+
function languageFromInterpreter(interpreter) {
|
|
3904
|
+
switch (interpreter) {
|
|
3905
|
+
case "sh":
|
|
3906
|
+
case "bash":
|
|
3907
|
+
case "zsh":
|
|
3908
|
+
case "dash":
|
|
3909
|
+
case "ksh":
|
|
3910
|
+
case "ash":
|
|
3911
|
+
return "bash";
|
|
3912
|
+
case "node":
|
|
3913
|
+
case "nodejs":
|
|
3914
|
+
return "javascript";
|
|
3915
|
+
case "python":
|
|
3916
|
+
case "python2":
|
|
3917
|
+
case "python3":
|
|
3918
|
+
return "python";
|
|
3919
|
+
case "ruby":
|
|
3920
|
+
return "ruby";
|
|
3921
|
+
case "php":
|
|
3922
|
+
return "php";
|
|
3923
|
+
case "lua":
|
|
3924
|
+
return "lua";
|
|
3925
|
+
default:
|
|
3926
|
+
return void 0;
|
|
3927
|
+
}
|
|
3874
3928
|
}
|
|
3875
3929
|
function formatDiagnosticCategory(category) {
|
|
3876
3930
|
switch (category) {
|
|
@@ -4472,8 +4526,11 @@ function inferCodeLanguage(filePath, mimeType = "", options = {}) {
|
|
|
4472
4526
|
if ([".cc", ".cpp", ".cxx", ".h", ".hh", ".hpp", ".hxx"].includes(extension)) {
|
|
4473
4527
|
return "cpp";
|
|
4474
4528
|
}
|
|
4475
|
-
if (!extension && options.executable
|
|
4476
|
-
|
|
4529
|
+
if (!extension && options.executable) {
|
|
4530
|
+
const fromShebang = languageFromInterpreter(interpreterFromShebang(options.content));
|
|
4531
|
+
if (fromShebang) {
|
|
4532
|
+
return fromShebang;
|
|
4533
|
+
}
|
|
4477
4534
|
}
|
|
4478
4535
|
return void 0;
|
|
4479
4536
|
}
|
|
@@ -4482,7 +4539,13 @@ function modulePageTitle(manifest) {
|
|
|
4482
4539
|
}
|
|
4483
4540
|
function importResolutionCandidates(basePath, specifier, extensions) {
|
|
4484
4541
|
const resolved = path6.posix.normalize(path6.posix.join(path6.posix.dirname(basePath), specifier));
|
|
4485
|
-
|
|
4542
|
+
const resolvedExt = path6.posix.extname(resolved);
|
|
4543
|
+
if (resolvedExt) {
|
|
4544
|
+
if (extensions.includes(resolvedExt)) {
|
|
4545
|
+
const resolvedBase = resolved.slice(0, -resolvedExt.length);
|
|
4546
|
+
const candidates = [resolved, ...extensions.map((extension) => `${resolvedBase}${extension}`)];
|
|
4547
|
+
return uniqueBy(candidates, (candidate) => candidate);
|
|
4548
|
+
}
|
|
4486
4549
|
return [resolved];
|
|
4487
4550
|
}
|
|
4488
4551
|
const direct = extensions.map((extension) => path6.posix.normalize(`${resolved}${extension}`));
|
|
@@ -4577,7 +4640,9 @@ async function readNearestGoModulePath(startPath, cache) {
|
|
|
4577
4640
|
}
|
|
4578
4641
|
function rustModuleAlias(repoRelativePath) {
|
|
4579
4642
|
const withoutExt = stripCodeExtension2(normalizeAlias(repoRelativePath));
|
|
4580
|
-
const
|
|
4643
|
+
const srcIdx = withoutExt.lastIndexOf("/src/");
|
|
4644
|
+
const withinCrate = srcIdx >= 0 ? withoutExt.slice(srcIdx + "/src/".length) : withoutExt.replace(/^src\//, "");
|
|
4645
|
+
const trimmed = withinCrate.replace(/\/mod$/i, "");
|
|
4581
4646
|
if (!trimmed || trimmed === "lib" || trimmed === "main") {
|
|
4582
4647
|
return "crate";
|
|
4583
4648
|
}
|
|
@@ -4792,7 +4857,7 @@ function resolveRustAliases(manifest, specifier) {
|
|
|
4792
4857
|
if (!currentAlias) {
|
|
4793
4858
|
return [];
|
|
4794
4859
|
}
|
|
4795
|
-
const currentParts = currentAlias.replace(/^crate
|
|
4860
|
+
const currentParts = currentAlias.replace(/^crate(?:::)?/, "").split("::").filter(Boolean);
|
|
4796
4861
|
if (specifier.startsWith("self::")) {
|
|
4797
4862
|
return [`crate${currentParts.length ? `::${currentParts.join("::")}` : ""}::${specifier.slice("self::".length)}`];
|
|
4798
4863
|
}
|
|
@@ -6583,7 +6648,7 @@ function inferKind(mimeType, filePath, detectionOptions = {}) {
|
|
|
6583
6648
|
if (mimeType === "text/csv" || mimeType === "text/tab-separated-values" || filePath.toLowerCase().endsWith(".csv") || filePath.toLowerCase().endsWith(".tsv")) {
|
|
6584
6649
|
return "csv";
|
|
6585
6650
|
}
|
|
6586
|
-
if (mimeType.startsWith("text/")) {
|
|
6651
|
+
if (mimeType.startsWith("text/") || isStructuredTextMime(mimeType)) {
|
|
6587
6652
|
return "text";
|
|
6588
6653
|
}
|
|
6589
6654
|
if (mimeType === "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" || filePath.toLowerCase().endsWith(".xlsx")) {
|
|
@@ -6597,6 +6662,26 @@ function inferKind(mimeType, filePath, detectionOptions = {}) {
|
|
|
6597
6662
|
}
|
|
6598
6663
|
return "binary";
|
|
6599
6664
|
}
|
|
6665
|
+
function isStructuredTextMime(mimeType) {
|
|
6666
|
+
switch (mimeType) {
|
|
6667
|
+
case "application/json":
|
|
6668
|
+
case "application/json5":
|
|
6669
|
+
case "application/ld+json":
|
|
6670
|
+
case "application/manifest+json":
|
|
6671
|
+
case "application/xml":
|
|
6672
|
+
case "application/toml":
|
|
6673
|
+
case "application/yaml":
|
|
6674
|
+
case "application/x-yaml":
|
|
6675
|
+
case "application/javascript":
|
|
6676
|
+
case "application/ecmascript":
|
|
6677
|
+
case "application/typescript":
|
|
6678
|
+
case "application/x-sh":
|
|
6679
|
+
case "application/x-shellscript":
|
|
6680
|
+
return true;
|
|
6681
|
+
default:
|
|
6682
|
+
return false;
|
|
6683
|
+
}
|
|
6684
|
+
}
|
|
6600
6685
|
async function localCodeDetectionOptions(absolutePath, payloadBytes) {
|
|
6601
6686
|
if (path12.extname(absolutePath)) {
|
|
6602
6687
|
return {};
|
|
@@ -6645,8 +6730,35 @@ function guessMimeType(target) {
|
|
|
6645
6730
|
if (isRstFilePath(target)) {
|
|
6646
6731
|
return "text/x-rst";
|
|
6647
6732
|
}
|
|
6733
|
+
const extension = path12.extname(target).toLowerCase();
|
|
6734
|
+
if (extension === ".ts" || extension === ".tsx" || extension === ".mts" || extension === ".cts") {
|
|
6735
|
+
return "text/typescript";
|
|
6736
|
+
}
|
|
6648
6737
|
return mime.lookup(target) || "application/octet-stream";
|
|
6649
6738
|
}
|
|
6739
|
+
function refineBinaryKindWithBytes(absolutePath, currentKind, bytes) {
|
|
6740
|
+
if (currentKind !== "binary") {
|
|
6741
|
+
return currentKind;
|
|
6742
|
+
}
|
|
6743
|
+
const sniffSlice = bytes.length > 4096 ? bytes.subarray(0, 4096) : bytes;
|
|
6744
|
+
return isText(absolutePath, sniffSlice) ? "text" : currentKind;
|
|
6745
|
+
}
|
|
6746
|
+
async function refineBinaryKindWithContentSniff(absolutePath, currentKind) {
|
|
6747
|
+
if (currentKind !== "binary") {
|
|
6748
|
+
return currentKind;
|
|
6749
|
+
}
|
|
6750
|
+
let handle;
|
|
6751
|
+
try {
|
|
6752
|
+
handle = await fs11.open(absolutePath, "r");
|
|
6753
|
+
const chunk = Buffer.alloc(4096);
|
|
6754
|
+
const { bytesRead } = await handle.read(chunk, 0, chunk.length, 0);
|
|
6755
|
+
return refineBinaryKindWithBytes(absolutePath, currentKind, chunk.subarray(0, bytesRead));
|
|
6756
|
+
} catch {
|
|
6757
|
+
return currentKind;
|
|
6758
|
+
} finally {
|
|
6759
|
+
await handle?.close().catch(() => void 0);
|
|
6760
|
+
}
|
|
6761
|
+
}
|
|
6650
6762
|
function sourceGroupIdFor(prepared) {
|
|
6651
6763
|
const originKey = prepared.originType === "url" ? prepared.url ?? prepared.title : prepared.originalPath ?? prepared.title;
|
|
6652
6764
|
return `${slugify(prepared.title)}-${sha256(originKey).slice(0, 8)}`;
|
|
@@ -7497,6 +7609,7 @@ async function collectDirectoryFiles(rootDir, inputDir, repoRoot, options) {
|
|
|
7497
7609
|
sourceKind = "chat_export";
|
|
7498
7610
|
}
|
|
7499
7611
|
}
|
|
7612
|
+
sourceKind = await refineBinaryKindWithContentSniff(absolutePath, sourceKind);
|
|
7500
7613
|
const sourceClass = sourceClassForRelativePath(relativePath, options);
|
|
7501
7614
|
if (!supportedDirectoryKind(sourceKind)) {
|
|
7502
7615
|
skipped.push({ path: toPosix(path12.relative(rootDir, absolutePath)), reason: `unsupported_kind:${sourceKind}` });
|
|
@@ -8118,7 +8231,7 @@ async function prepareFileInputs(rootDir, absoluteInput, repoRoot, sourceClass)
|
|
|
8118
8231
|
}
|
|
8119
8232
|
const mimeType = guessMimeType(absoluteInput);
|
|
8120
8233
|
const detectionOptions = await localCodeDetectionOptions(absoluteInput, payloadBytes);
|
|
8121
|
-
const sourceKind = inferKind(mimeType, absoluteInput, detectionOptions);
|
|
8234
|
+
const sourceKind = refineBinaryKindWithBytes(absoluteInput, inferKind(mimeType, absoluteInput, detectionOptions), payloadBytes);
|
|
8122
8235
|
const language = inferCodeLanguage(absoluteInput, mimeType, detectionOptions);
|
|
8123
8236
|
const storedExtension = path12.extname(absoluteInput) || `.${mime.extension(mimeType) || "bin"}`;
|
|
8124
8237
|
let title;
|
|
@@ -8564,7 +8677,7 @@ async function collectInboxAttachmentRefs(inputDir, files) {
|
|
|
8564
8677
|
const sourceRefs = [];
|
|
8565
8678
|
for (const ref of refs) {
|
|
8566
8679
|
const resolved = path12.resolve(path12.dirname(absolutePath), ref);
|
|
8567
|
-
if (!
|
|
8680
|
+
if (!isPathWithin(inputDir, resolved) || !await fileExists(resolved)) {
|
|
8568
8681
|
continue;
|
|
8569
8682
|
}
|
|
8570
8683
|
sourceRefs.push({
|
|
@@ -8902,6 +9015,7 @@ async function importInbox(rootDir, inputDir) {
|
|
|
8902
9015
|
sourceKind = "chat_export";
|
|
8903
9016
|
}
|
|
8904
9017
|
}
|
|
9018
|
+
sourceKind = await refineBinaryKindWithContentSniff(absolutePath, sourceKind);
|
|
8905
9019
|
if (!isSupportedInboxKind(sourceKind)) {
|
|
8906
9020
|
skipped.push({ path: toPosix(path12.relative(rootDir, absolutePath)), reason: `unsupported_kind:${sourceKind}` });
|
|
8907
9021
|
continue;
|
|
@@ -9099,6 +9213,7 @@ import { z as z7 } from "zod";
|
|
|
9099
9213
|
|
|
9100
9214
|
// src/analysis.ts
|
|
9101
9215
|
import path14 from "path";
|
|
9216
|
+
import nlp from "compromise";
|
|
9102
9217
|
import { fromMarkdown } from "mdast-util-from-markdown";
|
|
9103
9218
|
import { z as z2 } from "zod";
|
|
9104
9219
|
var ANALYSIS_FORMAT_VERSION = 7;
|
|
@@ -9176,11 +9291,31 @@ function extractTopTerms(text, count) {
|
|
|
9176
9291
|
return [...frequency.entries()].sort((left, right) => right[1] - left[1] || left[0].localeCompare(right[0])).slice(0, count).map(([token]) => token);
|
|
9177
9292
|
}
|
|
9178
9293
|
function extractEntities(text, count) {
|
|
9179
|
-
const
|
|
9180
|
-
|
|
9181
|
-
|
|
9182
|
-
|
|
9183
|
-
|
|
9294
|
+
const candidates = [];
|
|
9295
|
+
try {
|
|
9296
|
+
const doc = nlp(text);
|
|
9297
|
+
const segments = [
|
|
9298
|
+
doc.match("#ProperNoun+").out("array"),
|
|
9299
|
+
doc.people().out("array"),
|
|
9300
|
+
doc.places().out("array"),
|
|
9301
|
+
doc.organizations().out("array"),
|
|
9302
|
+
doc.topics().out("array")
|
|
9303
|
+
];
|
|
9304
|
+
for (const segment of segments) {
|
|
9305
|
+
for (const term of segment) {
|
|
9306
|
+
const normalized = normalizeWhitespace(term);
|
|
9307
|
+
if (normalized) {
|
|
9308
|
+
candidates.push(normalized);
|
|
9309
|
+
}
|
|
9310
|
+
}
|
|
9311
|
+
}
|
|
9312
|
+
} catch {
|
|
9313
|
+
}
|
|
9314
|
+
if (candidates.length === 0) {
|
|
9315
|
+
const matches = text.match(/\b[A-Z][A-Za-z0-9-]+(?:\s+[A-Z][A-Za-z0-9-]+){0,2}\b/g) ?? [];
|
|
9316
|
+
candidates.push(...matches.map((value) => normalizeWhitespace(value)));
|
|
9317
|
+
}
|
|
9318
|
+
return uniqueBy(candidates, (value) => value.toLowerCase()).slice(0, count);
|
|
9184
9319
|
}
|
|
9185
9320
|
function detectPolarity(text) {
|
|
9186
9321
|
if (/\b(no|not|never|cannot|can't|won't|without)\b/i.test(text)) {
|
|
@@ -10899,15 +11034,37 @@ function graphAdjacency(graph) {
|
|
|
10899
11034
|
}
|
|
10900
11035
|
return adjacency;
|
|
10901
11036
|
}
|
|
11037
|
+
var NODE_TYPE_PRIORITY = {
|
|
11038
|
+
concept: 6,
|
|
11039
|
+
entity: 5,
|
|
11040
|
+
source: 4,
|
|
11041
|
+
module: 3,
|
|
11042
|
+
symbol: 2,
|
|
11043
|
+
rationale: 1
|
|
11044
|
+
};
|
|
11045
|
+
function nodeTypePriority(type) {
|
|
11046
|
+
return NODE_TYPE_PRIORITY[type] ?? 0;
|
|
11047
|
+
}
|
|
11048
|
+
function compareLabelCandidates(left, right) {
|
|
11049
|
+
const priorityDelta = nodeTypePriority(right.type) - nodeTypePriority(left.type);
|
|
11050
|
+
if (priorityDelta !== 0) {
|
|
11051
|
+
return priorityDelta;
|
|
11052
|
+
}
|
|
11053
|
+
const degreeDelta = (right.degree ?? 0) - (left.degree ?? 0);
|
|
11054
|
+
if (degreeDelta !== 0) {
|
|
11055
|
+
return degreeDelta;
|
|
11056
|
+
}
|
|
11057
|
+
return left.id.localeCompare(right.id);
|
|
11058
|
+
}
|
|
10902
11059
|
function resolveNode(graph, target) {
|
|
10903
11060
|
const normalized = normalizeTarget(target);
|
|
10904
11061
|
const byId = nodeById(graph);
|
|
10905
11062
|
if (byId.has(target)) {
|
|
10906
11063
|
return byId.get(target);
|
|
10907
11064
|
}
|
|
10908
|
-
const
|
|
10909
|
-
if (
|
|
10910
|
-
return
|
|
11065
|
+
const labelMatches = graph.nodes.filter((node) => normalizeTarget(node.label) === normalized || normalizeTarget(node.id) === normalized);
|
|
11066
|
+
if (labelMatches.length) {
|
|
11067
|
+
return labelMatches.sort(compareLabelCandidates)[0];
|
|
10911
11068
|
}
|
|
10912
11069
|
const pages = graph.pages.map((page) => ({
|
|
10913
11070
|
page,
|
|
@@ -10916,7 +11073,7 @@ function resolveNode(graph, target) {
|
|
|
10916
11073
|
if (pages[0]) {
|
|
10917
11074
|
return primaryNodeForPage(graph, pages[0].page);
|
|
10918
11075
|
}
|
|
10919
|
-
return graph.nodes.map((node) => ({ node, score: Math.max(scoreMatch(target, node.label), scoreMatch(target, node.id)) })).filter((item) => item.score > 0).sort((left, right) => right.score - left.score || left.node
|
|
11076
|
+
return graph.nodes.map((node) => ({ node, score: Math.max(scoreMatch(target, node.label), scoreMatch(target, node.id)) })).filter((item) => item.score > 0).sort((left, right) => right.score - left.score || compareLabelCandidates(left.node, right.node))[0]?.node;
|
|
10920
11077
|
}
|
|
10921
11078
|
function communityLabel(graph, communityId) {
|
|
10922
11079
|
if (!communityId) {
|
|
@@ -13592,7 +13749,7 @@ async function resolveImageGenerationProvider(rootDir) {
|
|
|
13592
13749
|
if (!providerConfig) {
|
|
13593
13750
|
throw new Error(`No provider configured with id "${preferredProviderId}" for task "imageProvider".`);
|
|
13594
13751
|
}
|
|
13595
|
-
const { createProvider: createProvider2 } = await import("./registry-
|
|
13752
|
+
const { createProvider: createProvider2 } = await import("./registry-NBLIJHZT.js");
|
|
13596
13753
|
return createProvider2(preferredProviderId, providerConfig, rootDir);
|
|
13597
13754
|
}
|
|
13598
13755
|
async function generateOutputArtifacts(rootDir, input) {
|
|
@@ -17631,9 +17788,16 @@ async function listPages(rootDir) {
|
|
|
17631
17788
|
return graph?.pages ?? [];
|
|
17632
17789
|
}
|
|
17633
17790
|
async function readPage(rootDir, relativePath) {
|
|
17791
|
+
if (!relativePath) {
|
|
17792
|
+
return null;
|
|
17793
|
+
}
|
|
17634
17794
|
const { paths } = await loadVaultConfig(rootDir);
|
|
17635
17795
|
const absolutePath = path23.resolve(paths.wikiDir, relativePath);
|
|
17636
|
-
if (!
|
|
17796
|
+
if (!isPathWithin(paths.wikiDir, absolutePath)) {
|
|
17797
|
+
return null;
|
|
17798
|
+
}
|
|
17799
|
+
const stats = await fs19.stat(absolutePath).catch(() => null);
|
|
17800
|
+
if (!stats?.isFile()) {
|
|
17637
17801
|
return null;
|
|
17638
17802
|
}
|
|
17639
17803
|
const raw = await fs19.readFile(absolutePath, "utf8");
|
|
@@ -17662,6 +17826,32 @@ async function getWorkspaceInfo(rootDir) {
|
|
|
17662
17826
|
pageCount: pages.length
|
|
17663
17827
|
};
|
|
17664
17828
|
}
|
|
17829
|
+
function extractClaimSectionLines(content) {
|
|
17830
|
+
const lines = content.split("\n");
|
|
17831
|
+
let inClaims = false;
|
|
17832
|
+
let found = false;
|
|
17833
|
+
const claimLines = [];
|
|
17834
|
+
for (const line of lines) {
|
|
17835
|
+
const trimmed = line.trimEnd();
|
|
17836
|
+
if (trimmed === "## Claims") {
|
|
17837
|
+
inClaims = true;
|
|
17838
|
+
found = true;
|
|
17839
|
+
continue;
|
|
17840
|
+
}
|
|
17841
|
+
if (inClaims) {
|
|
17842
|
+
if (/^#{1,2}\s/.test(trimmed)) {
|
|
17843
|
+
inClaims = false;
|
|
17844
|
+
continue;
|
|
17845
|
+
}
|
|
17846
|
+
claimLines.push(line);
|
|
17847
|
+
}
|
|
17848
|
+
}
|
|
17849
|
+
return found ? claimLines : null;
|
|
17850
|
+
}
|
|
17851
|
+
function isClaimPlaceholderBullet(line) {
|
|
17852
|
+
const trimmed = line.trim();
|
|
17853
|
+
return /^-\s+No\s+claims\s+extracted\.?$/i.test(trimmed);
|
|
17854
|
+
}
|
|
17665
17855
|
function structuralLintFindings(_rootDir, paths, graph, schemas, manifests, sourceProjects) {
|
|
17666
17856
|
const manifestMap = new Map(manifests.map((manifest) => [manifest.sourceId, manifest]));
|
|
17667
17857
|
const pageMap2 = new Map(graph.pages.map((page) => [page.id, page]));
|
|
@@ -17707,8 +17897,11 @@ function structuralLintFindings(_rootDir, paths, graph, schemas, manifests, sour
|
|
|
17707
17897
|
const absolutePath = path23.join(paths.wikiDir, page.path);
|
|
17708
17898
|
if (await fileExists(absolutePath)) {
|
|
17709
17899
|
const content = await fs19.readFile(absolutePath, "utf8");
|
|
17710
|
-
|
|
17711
|
-
|
|
17900
|
+
const claimLines = extractClaimSectionLines(content);
|
|
17901
|
+
if (claimLines !== null) {
|
|
17902
|
+
const uncited = claimLines.filter(
|
|
17903
|
+
(line) => line.startsWith("- ") && !line.includes("[source:") && !isClaimPlaceholderBullet(line)
|
|
17904
|
+
);
|
|
17712
17905
|
if (uncited.length) {
|
|
17713
17906
|
findings.push({
|
|
17714
17907
|
severity: "warning",
|
|
@@ -17823,7 +18016,7 @@ async function bootstrapDemo(rootDir, input) {
|
|
|
17823
18016
|
}
|
|
17824
18017
|
|
|
17825
18018
|
// src/mcp.ts
|
|
17826
|
-
var SERVER_VERSION = "0.6.
|
|
18019
|
+
var SERVER_VERSION = "0.6.7";
|
|
17827
18020
|
async function createMcpServer(rootDir) {
|
|
17828
18021
|
const server = new McpServer({
|
|
17829
18022
|
name: "swarmvault",
|
|
@@ -18158,7 +18351,7 @@ async function createMcpServer(rootDir) {
|
|
|
18158
18351
|
const encodedPath = typeof variables.path === "string" ? variables.path : "";
|
|
18159
18352
|
const relativePath = decodeURIComponent(encodedPath);
|
|
18160
18353
|
const absolutePath = path24.resolve(paths.sessionsDir, relativePath);
|
|
18161
|
-
if (!
|
|
18354
|
+
if (!isPathWithin(paths.sessionsDir, absolutePath) || !await fileExists(absolutePath)) {
|
|
18162
18355
|
return asTextResource(`swarmvault://sessions/${encodedPath}`, `Session not found: ${relativePath}`);
|
|
18163
18356
|
}
|
|
18164
18357
|
return asTextResource(`swarmvault://sessions/${encodedPath}`, await fs20.readFile(absolutePath, "utf8"));
|
|
@@ -18497,6 +18690,15 @@ var DOCS_HINT_SEGMENTS = /* @__PURE__ */ new Set([
|
|
|
18497
18690
|
function uniqueStrings4(values) {
|
|
18498
18691
|
return uniqueBy(values.filter(Boolean), (value) => value);
|
|
18499
18692
|
}
|
|
18693
|
+
function sourceOutputSchemaHash(schemas, projectIds) {
|
|
18694
|
+
if (!projectIds.length) {
|
|
18695
|
+
return schemas.effective.global.hash;
|
|
18696
|
+
}
|
|
18697
|
+
return composeVaultSchema(
|
|
18698
|
+
schemas.root,
|
|
18699
|
+
uniqueStrings4([...projectIds].sort((left, right) => left.localeCompare(right))).map((projectId) => schemas.projects[projectId]).filter((schema) => Boolean(schema?.hash))
|
|
18700
|
+
).hash;
|
|
18701
|
+
}
|
|
18500
18702
|
function normalizeManagedStatus(value) {
|
|
18501
18703
|
return value === "missing" || value === "error" ? value : "ready";
|
|
18502
18704
|
}
|
|
@@ -19080,6 +19282,7 @@ async function writeSourceBriefForScope(rootDir, source) {
|
|
|
19080
19282
|
return null;
|
|
19081
19283
|
}
|
|
19082
19284
|
const graph = await readJsonFile(paths.graphPath);
|
|
19285
|
+
const schemas = await loadVaultSchemas(rootDir);
|
|
19083
19286
|
const relatedPages = graph ? scopedSourcePages(graph, source.sourceIds) : [];
|
|
19084
19287
|
const relatedPageIds = relatedPages.slice(0, 12).map((page) => page.id);
|
|
19085
19288
|
const relatedNodeIds = graph ? scopedNodeIds(graph, source.sourceIds).slice(0, 20) : [];
|
|
@@ -19090,7 +19293,7 @@ async function writeSourceBriefForScope(rootDir, source) {
|
|
|
19090
19293
|
question: `Brief ${source.title}`,
|
|
19091
19294
|
answer: markdown,
|
|
19092
19295
|
citations: source.sourceIds,
|
|
19093
|
-
schemaHash:
|
|
19296
|
+
schemaHash: sourceOutputSchemaHash(schemas, projectIds),
|
|
19094
19297
|
outputFormat: "report",
|
|
19095
19298
|
relatedPageIds,
|
|
19096
19299
|
relatedNodeIds,
|
|
@@ -19325,6 +19528,7 @@ async function buildSourceReviewStagedPage(rootDir, scope) {
|
|
|
19325
19528
|
throw new Error(`Could not generate a source review for ${scope.id}.`);
|
|
19326
19529
|
}
|
|
19327
19530
|
const graph = await readJsonFile(paths.graphPath);
|
|
19531
|
+
const schemas = await loadVaultSchemas(rootDir);
|
|
19328
19532
|
const scopeManifests = manifestsForScope(graph, scope);
|
|
19329
19533
|
const relatedPages = graph ? scopedSourcePages(graph, scope.sourceIds) : [];
|
|
19330
19534
|
const relatedPageIds = relatedPages.slice(0, 16).map((page) => page.id);
|
|
@@ -19336,7 +19540,7 @@ async function buildSourceReviewStagedPage(rootDir, scope) {
|
|
|
19336
19540
|
question: `Review ${scope.title}`,
|
|
19337
19541
|
answer: markdown,
|
|
19338
19542
|
citations: scope.sourceIds,
|
|
19339
|
-
schemaHash:
|
|
19543
|
+
schemaHash: sourceOutputSchemaHash(schemas, projectIds),
|
|
19340
19544
|
outputFormat: "report",
|
|
19341
19545
|
relatedPageIds,
|
|
19342
19546
|
relatedNodeIds,
|
|
@@ -19570,6 +19774,7 @@ async function buildSourceGuideStagedPage(rootDir, scope) {
|
|
|
19570
19774
|
throw new Error(`Could not generate a source guide for ${scope.id}.`);
|
|
19571
19775
|
}
|
|
19572
19776
|
const graph = await readJsonFile(paths.graphPath);
|
|
19777
|
+
const schemas = await loadVaultSchemas(rootDir);
|
|
19573
19778
|
const scopeManifests = manifestsForScope(graph, scope);
|
|
19574
19779
|
const relatedPages = graph ? scopedSourcePages(graph, scope.sourceIds) : [];
|
|
19575
19780
|
const contradictions = findContradictionsForScope(scope, await readGraphReport(rootDir));
|
|
@@ -19583,7 +19788,7 @@ async function buildSourceGuideStagedPage(rootDir, scope) {
|
|
|
19583
19788
|
question: `Guide ${scope.title}`,
|
|
19584
19789
|
answer: markdown,
|
|
19585
19790
|
citations: scope.sourceIds,
|
|
19586
|
-
schemaHash:
|
|
19791
|
+
schemaHash: sourceOutputSchemaHash(schemas, projectIds),
|
|
19587
19792
|
outputFormat: "report",
|
|
19588
19793
|
relatedPageIds,
|
|
19589
19794
|
relatedNodeIds,
|
|
@@ -19672,6 +19877,7 @@ async function buildSourceSessionSavedPage(rootDir, scope, session) {
|
|
|
19672
19877
|
await compileVault(rootDir, {});
|
|
19673
19878
|
graph = await readJsonFile(paths.graphPath);
|
|
19674
19879
|
}
|
|
19880
|
+
const schemas = await loadVaultSchemas(rootDir);
|
|
19675
19881
|
const scopeManifests = manifestsForScope(graph, scope);
|
|
19676
19882
|
const sourcePages = graph ? scopedSourcePages(graph, scope.sourceIds) : [];
|
|
19677
19883
|
const analyses = await loadSourceAnalyses(rootDir, scope.sourceIds);
|
|
@@ -19737,7 +19943,7 @@ async function buildSourceSessionSavedPage(rootDir, scope, session) {
|
|
|
19737
19943
|
question: `Guided Session ${scope.title}`,
|
|
19738
19944
|
answer: sessionMarkdown,
|
|
19739
19945
|
citations: scope.sourceIds,
|
|
19740
|
-
schemaHash:
|
|
19946
|
+
schemaHash: sourceOutputSchemaHash(schemas, projectIds),
|
|
19741
19947
|
outputFormat: "report",
|
|
19742
19948
|
relatedPageIds,
|
|
19743
19949
|
relatedNodeIds,
|
|
@@ -20782,10 +20988,21 @@ async function getWatchStatus(rootDir) {
|
|
|
20782
20988
|
|
|
20783
20989
|
// src/viewer.ts
|
|
20784
20990
|
var execFileAsync = promisify(execFile);
|
|
20991
|
+
async function isReadableFile(absolutePath) {
|
|
20992
|
+
try {
|
|
20993
|
+
const stats = await fs23.stat(absolutePath);
|
|
20994
|
+
return stats.isFile();
|
|
20995
|
+
} catch {
|
|
20996
|
+
return false;
|
|
20997
|
+
}
|
|
20998
|
+
}
|
|
20785
20999
|
async function readViewerPage(rootDir, relativePath) {
|
|
21000
|
+
if (!relativePath) {
|
|
21001
|
+
return null;
|
|
21002
|
+
}
|
|
20786
21003
|
const { paths } = await loadVaultConfig(rootDir);
|
|
20787
21004
|
const absolutePath = path28.resolve(paths.wikiDir, relativePath);
|
|
20788
|
-
if (!
|
|
21005
|
+
if (!isPathWithin(paths.wikiDir, absolutePath) || !await isReadableFile(absolutePath)) {
|
|
20789
21006
|
return null;
|
|
20790
21007
|
}
|
|
20791
21008
|
const raw = await fs23.readFile(absolutePath, "utf8");
|
|
@@ -20799,9 +21016,12 @@ async function readViewerPage(rootDir, relativePath) {
|
|
|
20799
21016
|
};
|
|
20800
21017
|
}
|
|
20801
21018
|
async function readViewerAsset(rootDir, relativePath) {
|
|
21019
|
+
if (!relativePath) {
|
|
21020
|
+
return null;
|
|
21021
|
+
}
|
|
20802
21022
|
const { paths } = await loadVaultConfig(rootDir);
|
|
20803
21023
|
const absolutePath = path28.resolve(paths.wikiDir, relativePath);
|
|
20804
|
-
if (!
|
|
21024
|
+
if (!isPathWithin(paths.wikiDir, absolutePath) || !await isReadableFile(absolutePath)) {
|
|
20805
21025
|
return null;
|
|
20806
21026
|
}
|
|
20807
21027
|
return {
|
|
@@ -20843,178 +21063,200 @@ async function startGraphServer(rootDir, port, options = {}) {
|
|
|
20843
21063
|
await ensureViewerDist(paths.viewerDistDir);
|
|
20844
21064
|
const server = http.createServer(async (request, response) => {
|
|
20845
21065
|
const url = new URL(request.url ?? "/", `http://${request.headers.host ?? `localhost:${effectivePort}`}`);
|
|
20846
|
-
|
|
20847
|
-
if (
|
|
20848
|
-
|
|
20849
|
-
|
|
21066
|
+
try {
|
|
21067
|
+
if (url.pathname === "/api/graph") {
|
|
21068
|
+
if (!await fileExists(paths.graphPath)) {
|
|
21069
|
+
response.writeHead(404, { "content-type": "application/json" });
|
|
21070
|
+
response.end(JSON.stringify({ error: "Graph artifact not found. Run `swarmvault compile` first." }));
|
|
21071
|
+
return;
|
|
21072
|
+
}
|
|
21073
|
+
const graph = await readJsonFile(paths.graphPath);
|
|
21074
|
+
if (!graph) {
|
|
21075
|
+
response.writeHead(404, { "content-type": "application/json" });
|
|
21076
|
+
response.end(JSON.stringify({ error: "Graph artifact not found. Run `swarmvault compile` first." }));
|
|
21077
|
+
return;
|
|
21078
|
+
}
|
|
21079
|
+
const reportPath = path28.join(paths.wikiDir, "graph", "report.json");
|
|
21080
|
+
const report = await readJsonFile(reportPath) ?? null;
|
|
21081
|
+
response.writeHead(200, { "content-type": "application/json" });
|
|
21082
|
+
response.end(JSON.stringify(buildViewerGraphArtifact(graph, { report, full: options.full ?? false })));
|
|
20850
21083
|
return;
|
|
20851
21084
|
}
|
|
20852
|
-
|
|
20853
|
-
|
|
20854
|
-
|
|
20855
|
-
|
|
21085
|
+
if (url.pathname === "/api/graph/query") {
|
|
21086
|
+
const question = url.searchParams.get("q") ?? "";
|
|
21087
|
+
const traversal = url.searchParams.get("traversal");
|
|
21088
|
+
const budget = Number.parseInt(url.searchParams.get("budget") ?? "12", 10);
|
|
21089
|
+
const result = await queryGraphVault(rootDir, question, {
|
|
21090
|
+
traversal: traversal === "dfs" ? "dfs" : "bfs",
|
|
21091
|
+
budget: Number.isFinite(budget) ? budget : 12
|
|
21092
|
+
});
|
|
21093
|
+
response.writeHead(200, { "content-type": "application/json" });
|
|
21094
|
+
response.end(JSON.stringify(result));
|
|
20856
21095
|
return;
|
|
20857
21096
|
}
|
|
20858
|
-
|
|
20859
|
-
|
|
20860
|
-
|
|
20861
|
-
|
|
20862
|
-
|
|
20863
|
-
|
|
20864
|
-
if (url.pathname === "/api/graph/query") {
|
|
20865
|
-
const question = url.searchParams.get("q") ?? "";
|
|
20866
|
-
const traversal = url.searchParams.get("traversal");
|
|
20867
|
-
const budget = Number.parseInt(url.searchParams.get("budget") ?? "12", 10);
|
|
20868
|
-
response.writeHead(200, { "content-type": "application/json" });
|
|
20869
|
-
response.end(
|
|
20870
|
-
JSON.stringify(
|
|
20871
|
-
await queryGraphVault(rootDir, question, {
|
|
20872
|
-
traversal: traversal === "dfs" ? "dfs" : "bfs",
|
|
20873
|
-
budget: Number.isFinite(budget) ? budget : 12
|
|
20874
|
-
})
|
|
20875
|
-
)
|
|
20876
|
-
);
|
|
20877
|
-
return;
|
|
20878
|
-
}
|
|
20879
|
-
if (url.pathname === "/api/graph/path") {
|
|
20880
|
-
const from = url.searchParams.get("from") ?? "";
|
|
20881
|
-
const to = url.searchParams.get("to") ?? "";
|
|
20882
|
-
response.writeHead(200, { "content-type": "application/json" });
|
|
20883
|
-
response.end(JSON.stringify(await pathGraphVault(rootDir, from, to)));
|
|
20884
|
-
return;
|
|
20885
|
-
}
|
|
20886
|
-
if (url.pathname === "/api/graph/explain") {
|
|
20887
|
-
const target2 = url.searchParams.get("target") ?? "";
|
|
20888
|
-
response.writeHead(200, { "content-type": "application/json" });
|
|
20889
|
-
response.end(JSON.stringify(await explainGraphVault(rootDir, target2)));
|
|
20890
|
-
return;
|
|
20891
|
-
}
|
|
20892
|
-
if (url.pathname === "/api/search") {
|
|
20893
|
-
if (!await fileExists(paths.searchDbPath)) {
|
|
20894
|
-
response.writeHead(404, { "content-type": "application/json" });
|
|
20895
|
-
response.end(JSON.stringify({ error: "Search index not found. Run `swarmvault compile` first." }));
|
|
21097
|
+
if (url.pathname === "/api/graph/path") {
|
|
21098
|
+
const from = url.searchParams.get("from") ?? "";
|
|
21099
|
+
const to = url.searchParams.get("to") ?? "";
|
|
21100
|
+
const result = await pathGraphVault(rootDir, from, to);
|
|
21101
|
+
response.writeHead(200, { "content-type": "application/json" });
|
|
21102
|
+
response.end(JSON.stringify(result));
|
|
20896
21103
|
return;
|
|
20897
21104
|
}
|
|
20898
|
-
|
|
20899
|
-
|
|
20900
|
-
|
|
20901
|
-
|
|
20902
|
-
|
|
20903
|
-
|
|
20904
|
-
|
|
20905
|
-
|
|
20906
|
-
|
|
20907
|
-
|
|
20908
|
-
status,
|
|
20909
|
-
project,
|
|
20910
|
-
sourceType,
|
|
20911
|
-
sourceClass
|
|
20912
|
-
});
|
|
20913
|
-
response.writeHead(200, { "content-type": "application/json" });
|
|
20914
|
-
response.end(JSON.stringify(results));
|
|
20915
|
-
return;
|
|
20916
|
-
}
|
|
20917
|
-
if (url.pathname === "/api/graph-report") {
|
|
20918
|
-
const reportPath = path28.join(paths.wikiDir, "graph", "report.json");
|
|
20919
|
-
if (!await fileExists(reportPath)) {
|
|
20920
|
-
response.writeHead(404, { "content-type": "application/json" });
|
|
20921
|
-
response.end(JSON.stringify({ error: "Graph report artifact not found. Run `swarmvault compile` first." }));
|
|
21105
|
+
if (url.pathname === "/api/graph/explain") {
|
|
21106
|
+
const target2 = url.searchParams.get("target") ?? "";
|
|
21107
|
+
if (!target2) {
|
|
21108
|
+
response.writeHead(400, { "content-type": "application/json" });
|
|
21109
|
+
response.end(JSON.stringify({ error: "Missing explain target." }));
|
|
21110
|
+
return;
|
|
21111
|
+
}
|
|
21112
|
+
const result = await explainGraphVault(rootDir, target2);
|
|
21113
|
+
response.writeHead(200, { "content-type": "application/json" });
|
|
21114
|
+
response.end(JSON.stringify(result));
|
|
20922
21115
|
return;
|
|
20923
21116
|
}
|
|
20924
|
-
|
|
20925
|
-
|
|
20926
|
-
|
|
20927
|
-
|
|
20928
|
-
|
|
20929
|
-
|
|
20930
|
-
|
|
20931
|
-
|
|
20932
|
-
|
|
20933
|
-
|
|
20934
|
-
|
|
20935
|
-
|
|
20936
|
-
|
|
20937
|
-
|
|
20938
|
-
|
|
21117
|
+
if (url.pathname === "/api/search") {
|
|
21118
|
+
if (!await fileExists(paths.searchDbPath)) {
|
|
21119
|
+
response.writeHead(404, { "content-type": "application/json" });
|
|
21120
|
+
response.end(JSON.stringify({ error: "Search index not found. Run `swarmvault compile` first." }));
|
|
21121
|
+
return;
|
|
21122
|
+
}
|
|
21123
|
+
const query = url.searchParams.get("q") ?? "";
|
|
21124
|
+
const limit = Number.parseInt(url.searchParams.get("limit") ?? "10", 10);
|
|
21125
|
+
const kind = url.searchParams.get("kind") ?? "all";
|
|
21126
|
+
const status = url.searchParams.get("status") ?? "all";
|
|
21127
|
+
const project = url.searchParams.get("project") ?? "all";
|
|
21128
|
+
const sourceType = url.searchParams.get("sourceType") ?? "all";
|
|
21129
|
+
const sourceClass = url.searchParams.get("sourceClass") ?? "all";
|
|
21130
|
+
const results = searchPages(paths.searchDbPath, query, {
|
|
21131
|
+
limit: Number.isFinite(limit) ? limit : 10,
|
|
21132
|
+
kind,
|
|
21133
|
+
status,
|
|
21134
|
+
project,
|
|
21135
|
+
sourceType,
|
|
21136
|
+
sourceClass
|
|
21137
|
+
});
|
|
21138
|
+
response.writeHead(200, { "content-type": "application/json" });
|
|
21139
|
+
response.end(JSON.stringify(results));
|
|
20939
21140
|
return;
|
|
20940
21141
|
}
|
|
20941
|
-
|
|
20942
|
-
|
|
20943
|
-
|
|
20944
|
-
|
|
20945
|
-
|
|
20946
|
-
|
|
20947
|
-
|
|
20948
|
-
|
|
20949
|
-
response.writeHead(
|
|
20950
|
-
response.end(
|
|
21142
|
+
if (url.pathname === "/api/graph-report") {
|
|
21143
|
+
const reportPath = path28.join(paths.wikiDir, "graph", "report.json");
|
|
21144
|
+
if (!await fileExists(reportPath)) {
|
|
21145
|
+
response.writeHead(404, { "content-type": "application/json" });
|
|
21146
|
+
response.end(JSON.stringify({ error: "Graph report artifact not found. Run `swarmvault compile` first." }));
|
|
21147
|
+
return;
|
|
21148
|
+
}
|
|
21149
|
+
const body = await fs23.readFile(reportPath, "utf8");
|
|
21150
|
+
response.writeHead(200, { "content-type": "application/json" });
|
|
21151
|
+
response.end(body);
|
|
20951
21152
|
return;
|
|
20952
21153
|
}
|
|
20953
|
-
|
|
20954
|
-
|
|
20955
|
-
|
|
20956
|
-
|
|
20957
|
-
if (url.pathname === "/api/reviews" && request.method === "GET") {
|
|
20958
|
-
response.writeHead(200, { "content-type": "application/json" });
|
|
20959
|
-
response.end(JSON.stringify(await listApprovals(rootDir)));
|
|
20960
|
-
return;
|
|
20961
|
-
}
|
|
20962
|
-
if (url.pathname === "/api/review" && request.method === "GET") {
|
|
20963
|
-
const approvalId = url.searchParams.get("id") ?? "";
|
|
20964
|
-
if (!approvalId) {
|
|
20965
|
-
response.writeHead(400, { "content-type": "application/json" });
|
|
20966
|
-
response.end(JSON.stringify({ error: "Missing approval id." }));
|
|
21154
|
+
if (url.pathname === "/api/watch-status") {
|
|
21155
|
+
const watchStatus = await getWatchStatus(rootDir);
|
|
21156
|
+
response.writeHead(200, { "content-type": "application/json" });
|
|
21157
|
+
response.end(JSON.stringify(watchStatus));
|
|
20967
21158
|
return;
|
|
20968
21159
|
}
|
|
20969
|
-
|
|
20970
|
-
|
|
20971
|
-
|
|
20972
|
-
|
|
20973
|
-
|
|
20974
|
-
|
|
20975
|
-
|
|
20976
|
-
|
|
20977
|
-
|
|
20978
|
-
|
|
20979
|
-
response.writeHead(400, { "content-type": "application/json" });
|
|
20980
|
-
response.end(JSON.stringify({ error: "Missing approval id or invalid review action." }));
|
|
21160
|
+
if (url.pathname === "/api/page") {
|
|
21161
|
+
const relativePath2 = url.searchParams.get("path") ?? "";
|
|
21162
|
+
const page = await readViewerPage(rootDir, relativePath2);
|
|
21163
|
+
if (!page) {
|
|
21164
|
+
response.writeHead(404, { "content-type": "application/json" });
|
|
21165
|
+
response.end(JSON.stringify({ error: `Page not found: ${relativePath2}` }));
|
|
21166
|
+
return;
|
|
21167
|
+
}
|
|
21168
|
+
response.writeHead(200, { "content-type": "application/json" });
|
|
21169
|
+
response.end(JSON.stringify(page));
|
|
20981
21170
|
return;
|
|
20982
21171
|
}
|
|
20983
|
-
|
|
20984
|
-
|
|
20985
|
-
|
|
20986
|
-
|
|
20987
|
-
|
|
20988
|
-
|
|
20989
|
-
|
|
20990
|
-
|
|
20991
|
-
|
|
20992
|
-
|
|
20993
|
-
if (url.pathname === "/api/candidate" && request.method === "POST") {
|
|
20994
|
-
const body = await readJsonBody(request);
|
|
20995
|
-
const target2 = typeof body.target === "string" ? body.target : "";
|
|
20996
|
-
const action = url.searchParams.get("action") ?? "";
|
|
20997
|
-
if (!target2 || action !== "promote" && action !== "archive") {
|
|
20998
|
-
response.writeHead(400, { "content-type": "application/json" });
|
|
20999
|
-
response.end(JSON.stringify({ error: "Missing candidate target or invalid candidate action." }));
|
|
21172
|
+
if (url.pathname === "/api/asset") {
|
|
21173
|
+
const relativePath2 = url.searchParams.get("path") ?? "";
|
|
21174
|
+
const asset = await readViewerAsset(rootDir, relativePath2);
|
|
21175
|
+
if (!asset) {
|
|
21176
|
+
response.writeHead(404, { "content-type": "application/json" });
|
|
21177
|
+
response.end(JSON.stringify({ error: `Asset not found: ${relativePath2}` }));
|
|
21178
|
+
return;
|
|
21179
|
+
}
|
|
21180
|
+
response.writeHead(200, { "content-type": asset.mimeType });
|
|
21181
|
+
response.end(asset.buffer);
|
|
21000
21182
|
return;
|
|
21001
21183
|
}
|
|
21002
|
-
|
|
21003
|
-
|
|
21004
|
-
|
|
21005
|
-
|
|
21006
|
-
|
|
21007
|
-
|
|
21008
|
-
|
|
21009
|
-
|
|
21010
|
-
|
|
21011
|
-
|
|
21012
|
-
|
|
21013
|
-
|
|
21014
|
-
|
|
21184
|
+
if (url.pathname === "/api/reviews" && request.method === "GET") {
|
|
21185
|
+
const approvals = await listApprovals(rootDir);
|
|
21186
|
+
response.writeHead(200, { "content-type": "application/json" });
|
|
21187
|
+
response.end(JSON.stringify(approvals));
|
|
21188
|
+
return;
|
|
21189
|
+
}
|
|
21190
|
+
if (url.pathname === "/api/review" && request.method === "GET") {
|
|
21191
|
+
const approvalId = url.searchParams.get("id") ?? "";
|
|
21192
|
+
if (!approvalId) {
|
|
21193
|
+
response.writeHead(400, { "content-type": "application/json" });
|
|
21194
|
+
response.end(JSON.stringify({ error: "Missing approval id." }));
|
|
21195
|
+
return;
|
|
21196
|
+
}
|
|
21197
|
+
const approval = await readApproval(rootDir, approvalId);
|
|
21198
|
+
response.writeHead(200, { "content-type": "application/json" });
|
|
21199
|
+
response.end(JSON.stringify(approval));
|
|
21200
|
+
return;
|
|
21201
|
+
}
|
|
21202
|
+
if (url.pathname === "/api/review" && request.method === "POST") {
|
|
21203
|
+
const body = await readJsonBody(request);
|
|
21204
|
+
const approvalId = typeof body.approvalId === "string" ? body.approvalId : "";
|
|
21205
|
+
const targets = Array.isArray(body.targets) ? body.targets.filter((item) => typeof item === "string") : [];
|
|
21206
|
+
const action = url.searchParams.get("action") ?? "";
|
|
21207
|
+
if (!approvalId || action !== "accept" && action !== "reject") {
|
|
21208
|
+
response.writeHead(400, { "content-type": "application/json" });
|
|
21209
|
+
response.end(JSON.stringify({ error: "Missing approval id or invalid review action." }));
|
|
21210
|
+
return;
|
|
21211
|
+
}
|
|
21212
|
+
const result = action === "accept" ? await acceptApproval(rootDir, approvalId, targets) : await rejectApproval(rootDir, approvalId, targets);
|
|
21213
|
+
response.writeHead(200, { "content-type": "application/json" });
|
|
21214
|
+
response.end(JSON.stringify(result));
|
|
21215
|
+
return;
|
|
21216
|
+
}
|
|
21217
|
+
if (url.pathname === "/api/candidates" && request.method === "GET") {
|
|
21218
|
+
const candidates = await listCandidates(rootDir);
|
|
21219
|
+
response.writeHead(200, { "content-type": "application/json" });
|
|
21220
|
+
response.end(JSON.stringify(candidates));
|
|
21221
|
+
return;
|
|
21222
|
+
}
|
|
21223
|
+
if (url.pathname === "/api/candidate" && request.method === "POST") {
|
|
21224
|
+
const body = await readJsonBody(request);
|
|
21225
|
+
const target2 = typeof body.target === "string" ? body.target : "";
|
|
21226
|
+
const action = url.searchParams.get("action") ?? "";
|
|
21227
|
+
if (!target2 || action !== "promote" && action !== "archive") {
|
|
21228
|
+
response.writeHead(400, { "content-type": "application/json" });
|
|
21229
|
+
response.end(JSON.stringify({ error: "Missing candidate target or invalid candidate action." }));
|
|
21230
|
+
return;
|
|
21231
|
+
}
|
|
21232
|
+
const result = action === "promote" ? await promoteCandidate(rootDir, target2) : await archiveCandidate(rootDir, target2);
|
|
21233
|
+
response.writeHead(200, { "content-type": "application/json" });
|
|
21234
|
+
response.end(JSON.stringify(result));
|
|
21235
|
+
return;
|
|
21236
|
+
}
|
|
21237
|
+
const relativePath = url.pathname === "/" ? "index.html" : url.pathname.slice(1);
|
|
21238
|
+
const target = path28.join(paths.viewerDistDir, relativePath);
|
|
21239
|
+
const fallback = path28.join(paths.viewerDistDir, "index.html");
|
|
21240
|
+
const filePath = await fileExists(target) ? target : fallback;
|
|
21241
|
+
if (!await fileExists(filePath)) {
|
|
21242
|
+
response.writeHead(503, { "content-type": "text/plain" });
|
|
21243
|
+
response.end("Viewer build not found. Run `pnpm build` first.");
|
|
21244
|
+
return;
|
|
21245
|
+
}
|
|
21246
|
+
const staticBody = await fs23.readFile(filePath);
|
|
21247
|
+
response.writeHead(200, { "content-type": mime2.lookup(filePath) || "text/plain" });
|
|
21248
|
+
response.end(staticBody);
|
|
21249
|
+
} catch (error) {
|
|
21250
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
21251
|
+
console.error(`[viewer] ${request.method ?? "GET"} ${url.pathname} failed: ${message}`);
|
|
21252
|
+
if (!response.headersSent) {
|
|
21253
|
+
const status = /not found|could not resolve|cannot resolve/i.test(message) ? 404 : 500;
|
|
21254
|
+
response.writeHead(status, { "content-type": "application/json" });
|
|
21255
|
+
response.end(JSON.stringify({ error: message }));
|
|
21256
|
+
} else {
|
|
21257
|
+
response.end();
|
|
21258
|
+
}
|
|
21015
21259
|
}
|
|
21016
|
-
response.writeHead(200, { "content-type": mime2.lookup(filePath) || "text/plain" });
|
|
21017
|
-
response.end(await fs23.readFile(filePath));
|
|
21018
21260
|
});
|
|
21019
21261
|
await new Promise((resolve) => {
|
|
21020
21262
|
server.listen(effectivePort, resolve);
|