@swarmvaultai/engine 0.1.28 → 0.1.29
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/README.md +2 -1
- package/dist/chunk-IHMJCCXR.js +1146 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +531 -103
- package/dist/registry-5SYH3Y3U.js +12 -0
- package/dist/viewer/assets/index-Csm8eB3P.js +331 -0
- package/dist/viewer/assets/index-DUJ6MWHL.css +1 -0
- package/dist/viewer/index.html +2 -2
- package/dist/viewer/lib.js +178 -0
- package/package.json +5 -3
- package/dist/viewer/assets/index-C7PCTMog.js +0 -330
- package/dist/viewer/assets/index-DiMCbjBi.css +0 -1
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-IHMJCCXR.js";
|
|
25
25
|
|
|
26
26
|
// src/agents.ts
|
|
27
27
|
import crypto from "crypto";
|
|
@@ -32,8 +32,8 @@ var managedStart = "<!-- swarmvault:managed:start -->";
|
|
|
32
32
|
var managedEnd = "<!-- swarmvault:managed:end -->";
|
|
33
33
|
var legacyManagedStart = "<!-- vault:managed:start -->";
|
|
34
34
|
var legacyManagedEnd = "<!-- vault:managed:end -->";
|
|
35
|
-
var
|
|
36
|
-
var
|
|
35
|
+
var claudeSearchMatcher = "Glob|Grep";
|
|
36
|
+
var claudeSessionMatchers = ["startup", "resume", "clear", "compact"];
|
|
37
37
|
var geminiSessionMatcher = "startup";
|
|
38
38
|
var geminiSearchMatcher = "glob|grep|search|find";
|
|
39
39
|
var copilotHookVersion = 1;
|
|
@@ -89,6 +89,8 @@ function primaryTargetPathForAgent(rootDir, agent) {
|
|
|
89
89
|
}
|
|
90
90
|
function hookScriptPathForAgent(rootDir, agent) {
|
|
91
91
|
switch (agent) {
|
|
92
|
+
case "claude":
|
|
93
|
+
return path.join(rootDir, ".claude", "hooks", "swarmvault-graph-first.js");
|
|
92
94
|
case "opencode":
|
|
93
95
|
return path.join(rootDir, ".opencode", "plugins", "swarmvault-graph-first.js");
|
|
94
96
|
case "gemini":
|
|
@@ -177,25 +179,106 @@ async function readJsonWithWarnings(filePath, fallback, label) {
|
|
|
177
179
|
}
|
|
178
180
|
async function installClaudeHook(rootDir) {
|
|
179
181
|
const settingsPath = path.join(rootDir, ".claude", "settings.json");
|
|
182
|
+
const scriptPath = path.join(rootDir, ".claude", "hooks", "swarmvault-graph-first.js");
|
|
183
|
+
await writeOwnedFile(scriptPath, buildClaudeHookScript(), true);
|
|
180
184
|
await ensureDir(path.dirname(settingsPath));
|
|
181
185
|
const { data: settings, warnings } = await readJsonWithWarnings(settingsPath, {}, ".claude/settings.json");
|
|
182
186
|
if (warnings.length > 0 && await fileExists(settingsPath)) {
|
|
183
187
|
return { path: settingsPath, warnings };
|
|
184
188
|
}
|
|
185
189
|
const hooks = settings.hooks ?? {};
|
|
190
|
+
const sessionStart = hooks.SessionStart ?? [];
|
|
186
191
|
const preToolUse = hooks.PreToolUse ?? [];
|
|
187
|
-
const
|
|
188
|
-
|
|
192
|
+
const sessionCommand = 'node "$CLAUDE_PROJECT_DIR/.claude/hooks/swarmvault-graph-first.js" session-start';
|
|
193
|
+
const preToolUseCommand = 'node "$CLAUDE_PROJECT_DIR/.claude/hooks/swarmvault-graph-first.js" pre-tool-use';
|
|
194
|
+
for (const matcher of claudeSessionMatchers) {
|
|
195
|
+
if (!sessionStart.some((entry) => entry.matcher === matcher && JSON.stringify(entry).includes("swarmvault-graph-first.js"))) {
|
|
196
|
+
sessionStart.push({
|
|
197
|
+
matcher,
|
|
198
|
+
hooks: [{ type: "command", command: sessionCommand }]
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
if (!preToolUse.some((entry) => entry.matcher === claudeSearchMatcher && JSON.stringify(entry).includes("swarmvault-graph-first.js"))) {
|
|
189
203
|
preToolUse.push({
|
|
190
|
-
matcher:
|
|
191
|
-
hooks: [{ type: "command", command:
|
|
204
|
+
matcher: claudeSearchMatcher,
|
|
205
|
+
hooks: [{ type: "command", command: preToolUseCommand }]
|
|
192
206
|
});
|
|
193
207
|
}
|
|
194
|
-
settings.hooks = { ...hooks, PreToolUse: preToolUse };
|
|
208
|
+
settings.hooks = { ...hooks, SessionStart: sessionStart, PreToolUse: preToolUse };
|
|
195
209
|
await fs.writeFile(settingsPath, `${JSON.stringify(settings, null, 2)}
|
|
196
210
|
`, "utf8");
|
|
197
211
|
return { path: settingsPath, warnings: [] };
|
|
198
212
|
}
|
|
213
|
+
function buildClaudeHookScript() {
|
|
214
|
+
return `#!/usr/bin/env node
|
|
215
|
+
import crypto from "node:crypto";
|
|
216
|
+
import fs from "node:fs/promises";
|
|
217
|
+
import os from "node:os";
|
|
218
|
+
import path from "node:path";
|
|
219
|
+
|
|
220
|
+
${markerStateSnippet("claude").trim()}
|
|
221
|
+
|
|
222
|
+
async function readInput() {
|
|
223
|
+
let body = "";
|
|
224
|
+
for await (const chunk of process.stdin) {
|
|
225
|
+
body += chunk;
|
|
226
|
+
}
|
|
227
|
+
if (!body.trim()) {
|
|
228
|
+
return {};
|
|
229
|
+
}
|
|
230
|
+
try {
|
|
231
|
+
return JSON.parse(body);
|
|
232
|
+
} catch {
|
|
233
|
+
return {};
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function emit(value) {
|
|
238
|
+
process.stdout.write(\`\${JSON.stringify(value)}\\n\`);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const mode = process.argv[2] ?? "";
|
|
242
|
+
const input = await readInput();
|
|
243
|
+
const cwd = resolveInputCwd(input);
|
|
244
|
+
const reportNote = "SwarmVault graph report exists at wiki/graph/report.md. Read it before broad grep/glob searching.";
|
|
245
|
+
|
|
246
|
+
if (!(await hasReport(cwd))) {
|
|
247
|
+
emit({});
|
|
248
|
+
process.exit(0);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
if (mode === "session-start") {
|
|
252
|
+
await resetSession(cwd);
|
|
253
|
+
emit({
|
|
254
|
+
hookSpecificOutput: {
|
|
255
|
+
hookEventName: "SessionStart",
|
|
256
|
+
additionalContext: reportNote
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
process.exit(0);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const toolName = resolveToolName(input);
|
|
263
|
+
if (collectCandidatePaths(input).some((value) => isReportPath(value, cwd))) {
|
|
264
|
+
await markReportRead(cwd);
|
|
265
|
+
emit({});
|
|
266
|
+
process.exit(0);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
if (isBroadSearchTool(toolName) && !(await hasSeenReport(cwd))) {
|
|
270
|
+
emit({
|
|
271
|
+
hookSpecificOutput: {
|
|
272
|
+
hookEventName: "PreToolUse",
|
|
273
|
+
additionalContext: reportNote
|
|
274
|
+
}
|
|
275
|
+
});
|
|
276
|
+
process.exit(0);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
emit({});
|
|
280
|
+
`;
|
|
281
|
+
}
|
|
199
282
|
function markerStateSnippet(agentKey) {
|
|
200
283
|
return `
|
|
201
284
|
function markerState(cwd) {
|
|
@@ -237,7 +320,9 @@ function collectCandidatePaths(node, acc = []) {
|
|
|
237
320
|
for (const [key, value] of Object.entries(node)) {
|
|
238
321
|
if (["path", "filePath", "file_path", "paths", "target", "targets"].includes(key)) {
|
|
239
322
|
collectCandidatePaths(value, acc);
|
|
323
|
+
continue;
|
|
240
324
|
}
|
|
325
|
+
collectCandidatePaths(value, acc);
|
|
241
326
|
}
|
|
242
327
|
return acc;
|
|
243
328
|
}
|
|
@@ -674,8 +759,38 @@ var NODE_COLORS = {
|
|
|
674
759
|
function xmlEscape(value) {
|
|
675
760
|
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
676
761
|
}
|
|
677
|
-
function
|
|
678
|
-
|
|
762
|
+
function cypherStringLiteral(value) {
|
|
763
|
+
let escaped = "";
|
|
764
|
+
for (const char of value) {
|
|
765
|
+
switch (char) {
|
|
766
|
+
case "\\":
|
|
767
|
+
escaped += "\\\\";
|
|
768
|
+
break;
|
|
769
|
+
case "'":
|
|
770
|
+
escaped += "\\'";
|
|
771
|
+
break;
|
|
772
|
+
case "\n":
|
|
773
|
+
escaped += "\\n";
|
|
774
|
+
break;
|
|
775
|
+
case "\r":
|
|
776
|
+
escaped += "\\r";
|
|
777
|
+
break;
|
|
778
|
+
case " ":
|
|
779
|
+
escaped += "\\t";
|
|
780
|
+
break;
|
|
781
|
+
case "\b":
|
|
782
|
+
escaped += "\\b";
|
|
783
|
+
break;
|
|
784
|
+
case "\f":
|
|
785
|
+
escaped += "\\f";
|
|
786
|
+
break;
|
|
787
|
+
default: {
|
|
788
|
+
const code = char.codePointAt(0) ?? 0;
|
|
789
|
+
escaped += code < 32 || code === 8232 || code === 8233 ? `\\u${code.toString(16).padStart(4, "0")}` : char;
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
return `'${escaped}'`;
|
|
679
794
|
}
|
|
680
795
|
function relationType(relation) {
|
|
681
796
|
const normalized = relation.toUpperCase().replace(/[^A-Z0-9]+/g, "_").replace(/^_+|_+$/g, "");
|
|
@@ -951,33 +1066,33 @@ function renderCypher(graph) {
|
|
|
951
1066
|
for (const node of [...graph.nodes].sort((left, right) => left.id.localeCompare(right.id))) {
|
|
952
1067
|
const page = node.pageId ? pageById2.get(node.pageId) : void 0;
|
|
953
1068
|
const props = [
|
|
954
|
-
`id:
|
|
955
|
-
`label:
|
|
956
|
-
`type:
|
|
957
|
-
`sourceIds:
|
|
958
|
-
`projectIds:
|
|
959
|
-
node.pageId ? `pageId:
|
|
960
|
-
page?.path ? `pagePath:
|
|
961
|
-
node.language ? `language:
|
|
962
|
-
node.symbolKind ? `symbolKind:
|
|
963
|
-
node.communityId ? `communityId:
|
|
1069
|
+
`id: ${cypherStringLiteral(node.id)}`,
|
|
1070
|
+
`label: ${cypherStringLiteral(node.label)}`,
|
|
1071
|
+
`type: ${cypherStringLiteral(node.type)}`,
|
|
1072
|
+
`sourceIds: ${cypherStringLiteral(JSON.stringify(node.sourceIds))}`,
|
|
1073
|
+
`projectIds: ${cypherStringLiteral(JSON.stringify(node.projectIds))}`,
|
|
1074
|
+
node.pageId ? `pageId: ${cypherStringLiteral(node.pageId)}` : "",
|
|
1075
|
+
page?.path ? `pagePath: ${cypherStringLiteral(page.path)}` : "",
|
|
1076
|
+
node.language ? `language: ${cypherStringLiteral(node.language)}` : "",
|
|
1077
|
+
node.symbolKind ? `symbolKind: ${cypherStringLiteral(node.symbolKind)}` : "",
|
|
1078
|
+
node.communityId ? `communityId: ${cypherStringLiteral(node.communityId)}` : "",
|
|
964
1079
|
node.degree !== void 0 ? `degree: ${node.degree}` : "",
|
|
965
1080
|
node.bridgeScore !== void 0 ? `bridgeScore: ${node.bridgeScore}` : "",
|
|
966
1081
|
node.isGodNode !== void 0 ? `isGodNode: ${node.isGodNode}` : ""
|
|
967
1082
|
].filter(Boolean).join(", ");
|
|
968
|
-
lines.push(`MERGE (n:SwarmNode {id:
|
|
1083
|
+
lines.push(`MERGE (n:SwarmNode {id: ${cypherStringLiteral(node.id)}}) SET n += { ${props} };`);
|
|
969
1084
|
}
|
|
970
1085
|
lines.push("");
|
|
971
1086
|
for (const hyperedge of [...graph.hyperedges ?? []].sort((left, right) => left.id.localeCompare(right.id))) {
|
|
972
1087
|
const hyperedgeNodeId = exportHyperedgeNodeId(hyperedge);
|
|
973
1088
|
lines.push(
|
|
974
|
-
`MERGE (h:SwarmNode {id:
|
|
1089
|
+
`MERGE (h:SwarmNode {id: ${cypherStringLiteral(hyperedgeNodeId)}}) SET h += { id: ${cypherStringLiteral(hyperedgeNodeId)}, label: ${cypherStringLiteral(
|
|
975
1090
|
hyperedge.label
|
|
976
|
-
)}
|
|
1091
|
+
)}, type: ${cypherStringLiteral("hyperedge")}, relation: ${cypherStringLiteral(hyperedge.relation)}, evidenceClass: ${cypherStringLiteral(
|
|
977
1092
|
hyperedge.evidenceClass
|
|
978
|
-
)}
|
|
1093
|
+
)}, confidence: ${hyperedge.confidence}, sourcePageIds: ${cypherStringLiteral(JSON.stringify(hyperedge.sourcePageIds))}, why: ${cypherStringLiteral(
|
|
979
1094
|
hyperedge.why
|
|
980
|
-
)}
|
|
1095
|
+
)} };`
|
|
981
1096
|
);
|
|
982
1097
|
}
|
|
983
1098
|
if ((graph.hyperedges ?? []).length) {
|
|
@@ -987,22 +1102,22 @@ function renderCypher(graph) {
|
|
|
987
1102
|
const hyperedgeNodeId = exportHyperedgeNodeId(hyperedge);
|
|
988
1103
|
for (const nodeId of hyperedge.nodeIds) {
|
|
989
1104
|
lines.push(
|
|
990
|
-
`MATCH (h:SwarmNode {id:
|
|
991
|
-
`MERGE (h)-[r:GROUP_MEMBER {id:
|
|
992
|
-
`SET r += { relation:
|
|
1105
|
+
`MATCH (h:SwarmNode {id: ${cypherStringLiteral(hyperedgeNodeId)}}), (n:SwarmNode {id: ${cypherStringLiteral(nodeId)}})`,
|
|
1106
|
+
`MERGE (h)-[r:GROUP_MEMBER {id: ${cypherStringLiteral(`member:${hyperedge.id}:${nodeId}`)}}]->(n)`,
|
|
1107
|
+
`SET r += { relation: ${cypherStringLiteral("group_member")}, status: ${cypherStringLiteral("inferred")}, evidenceClass: ${cypherStringLiteral(
|
|
993
1108
|
hyperedge.evidenceClass
|
|
994
|
-
)}
|
|
1109
|
+
)}, confidence: ${hyperedge.confidence}, provenance: ${cypherStringLiteral(JSON.stringify(hyperedge.sourcePageIds))} };`
|
|
995
1110
|
);
|
|
996
1111
|
}
|
|
997
1112
|
}
|
|
998
1113
|
lines.push("");
|
|
999
1114
|
for (const edge of [...graph.edges].sort((left, right) => left.id.localeCompare(right.id))) {
|
|
1000
1115
|
lines.push(
|
|
1001
|
-
`MATCH (a:SwarmNode {id:
|
|
1002
|
-
`MERGE (a)-[r:${relationType(edge.relation)} {id:
|
|
1003
|
-
`SET r += { relation:
|
|
1116
|
+
`MATCH (a:SwarmNode {id: ${cypherStringLiteral(edge.source)}}), (b:SwarmNode {id: ${cypherStringLiteral(edge.target)}})`,
|
|
1117
|
+
`MERGE (a)-[r:${relationType(edge.relation)} {id: ${cypherStringLiteral(edge.id)}}]->(b)`,
|
|
1118
|
+
`SET r += { relation: ${cypherStringLiteral(edge.relation)}, status: ${cypherStringLiteral(edge.status)}, evidenceClass: ${cypherStringLiteral(
|
|
1004
1119
|
edge.evidenceClass
|
|
1005
|
-
)}
|
|
1120
|
+
)}, confidence: ${edge.confidence}, provenance: ${cypherStringLiteral(JSON.stringify(edge.provenance))}${edge.similarityReasons?.length ? `, similarityReasons: ${cypherStringLiteral(JSON.stringify(edge.similarityReasons))}` : ""} };`
|
|
1006
1121
|
);
|
|
1007
1122
|
}
|
|
1008
1123
|
lines.push("");
|
|
@@ -1173,7 +1288,7 @@ import { pathToFileURL } from "url";
|
|
|
1173
1288
|
import { Readability } from "@mozilla/readability";
|
|
1174
1289
|
import matter3 from "gray-matter";
|
|
1175
1290
|
import ignore from "ignore";
|
|
1176
|
-
import { JSDOM } from "jsdom";
|
|
1291
|
+
import { JSDOM as JSDOM2 } from "jsdom";
|
|
1177
1292
|
import mime from "mime-types";
|
|
1178
1293
|
import TurndownService from "turndown";
|
|
1179
1294
|
|
|
@@ -1298,6 +1413,55 @@ function collectCallNamesFromText(text, availableNames, selfName) {
|
|
|
1298
1413
|
}
|
|
1299
1414
|
return uniqueBy(names, (name) => name);
|
|
1300
1415
|
}
|
|
1416
|
+
function goReceiverBinding(node) {
|
|
1417
|
+
if (!node) {
|
|
1418
|
+
return {};
|
|
1419
|
+
}
|
|
1420
|
+
const names = uniqueBy(
|
|
1421
|
+
node.descendantsOfType(["identifier", "type_identifier"]).filter((item) => item !== null).map((item) => normalizeSymbolReference(item.text)).filter(Boolean),
|
|
1422
|
+
(item) => item
|
|
1423
|
+
);
|
|
1424
|
+
if (names.length === 0) {
|
|
1425
|
+
return {};
|
|
1426
|
+
}
|
|
1427
|
+
if (names.length === 1) {
|
|
1428
|
+
return { typeName: names[0] };
|
|
1429
|
+
}
|
|
1430
|
+
return {
|
|
1431
|
+
variableName: names[0],
|
|
1432
|
+
typeName: names.at(-1)
|
|
1433
|
+
};
|
|
1434
|
+
}
|
|
1435
|
+
function goCalledSymbolName(node, receiver) {
|
|
1436
|
+
if (!node) {
|
|
1437
|
+
return void 0;
|
|
1438
|
+
}
|
|
1439
|
+
if (node.type === "selector_expression") {
|
|
1440
|
+
const targetName = normalizeSymbolReference(
|
|
1441
|
+
extractIdentifier(node.childForFieldName("operand") ?? node.childForFieldName("object") ?? node.namedChildren.at(0) ?? null) ?? ""
|
|
1442
|
+
);
|
|
1443
|
+
const fieldName = normalizeSymbolReference(
|
|
1444
|
+
extractIdentifier(node.childForFieldName("field") ?? findNamedChild(node, "field_identifier") ?? node.namedChildren.at(-1) ?? null) ?? ""
|
|
1445
|
+
);
|
|
1446
|
+
if (!fieldName) {
|
|
1447
|
+
return void 0;
|
|
1448
|
+
}
|
|
1449
|
+
if (receiver.variableName && receiver.typeName && targetName === receiver.variableName) {
|
|
1450
|
+
return `${receiver.typeName}.${fieldName}`;
|
|
1451
|
+
}
|
|
1452
|
+
return fieldName;
|
|
1453
|
+
}
|
|
1454
|
+
return normalizeSymbolReference(extractIdentifier(node) ?? "");
|
|
1455
|
+
}
|
|
1456
|
+
function goCallNamesFromBody(bodyNode, receiver) {
|
|
1457
|
+
if (!bodyNode) {
|
|
1458
|
+
return [];
|
|
1459
|
+
}
|
|
1460
|
+
return uniqueBy(
|
|
1461
|
+
bodyNode.descendantsOfType("call_expression").filter((item) => item !== null).map((callNode) => goCalledSymbolName(callNode.childForFieldName("function") ?? callNode.namedChildren.at(0) ?? null, receiver)).filter((name) => Boolean(name)),
|
|
1462
|
+
(name) => name
|
|
1463
|
+
);
|
|
1464
|
+
}
|
|
1301
1465
|
function finalizeCodeAnalysis(manifest, language, imports, draftSymbols, exportLabels, diagnostics, metadata) {
|
|
1302
1466
|
const topLevelNames = new Set(draftSymbols.map((symbol) => symbol.name));
|
|
1303
1467
|
for (const symbol of draftSymbols) {
|
|
@@ -1784,7 +1948,9 @@ function goCodeAnalysis(manifest, rootNode, diagnostics) {
|
|
|
1784
1948
|
if (!name) {
|
|
1785
1949
|
continue;
|
|
1786
1950
|
}
|
|
1787
|
-
const
|
|
1951
|
+
const receiver = child.type === "method_declaration" ? goReceiverBinding(child.childForFieldName("receiver")) : {};
|
|
1952
|
+
const receiverType = receiver.typeName ?? "";
|
|
1953
|
+
const bodyNode = child.childForFieldName("body");
|
|
1788
1954
|
const symbolName = receiverType ? `${receiverType}.${name}` : name;
|
|
1789
1955
|
const exported = exportedByCapitalization(name);
|
|
1790
1956
|
draftSymbols.push({
|
|
@@ -1792,10 +1958,10 @@ function goCodeAnalysis(manifest, rootNode, diagnostics) {
|
|
|
1792
1958
|
kind: "function",
|
|
1793
1959
|
signature: singleLineSignature(child.text),
|
|
1794
1960
|
exported,
|
|
1795
|
-
callNames:
|
|
1961
|
+
callNames: goCallNamesFromBody(bodyNode, receiver),
|
|
1796
1962
|
extendsNames: [],
|
|
1797
1963
|
implementsNames: [],
|
|
1798
|
-
bodyText: nodeText(
|
|
1964
|
+
bodyText: nodeText(bodyNode)
|
|
1799
1965
|
});
|
|
1800
1966
|
if (exported) {
|
|
1801
1967
|
exportLabels.push(symbolName);
|
|
@@ -3325,6 +3491,8 @@ async function analyzeCodeSource(manifest, extractedText, schemaHash) {
|
|
|
3325
3491
|
import fs6 from "fs/promises";
|
|
3326
3492
|
import os from "os";
|
|
3327
3493
|
import path6 from "path";
|
|
3494
|
+
import { strFromU8, unzipSync } from "fflate";
|
|
3495
|
+
import { JSDOM } from "jsdom";
|
|
3328
3496
|
import { z } from "zod";
|
|
3329
3497
|
var imageVisionExtractionSchema = z.object({
|
|
3330
3498
|
title: z.string().min(1).nullable().optional(),
|
|
@@ -3501,6 +3669,49 @@ function normalizePdfMetadata(raw) {
|
|
|
3501
3669
|
}
|
|
3502
3670
|
return Object.keys(metadata).length ? metadata : void 0;
|
|
3503
3671
|
}
|
|
3672
|
+
function normalizeDocumentText(raw) {
|
|
3673
|
+
return raw.replace(/\r\n/g, "\n").split(/\n{2,}/).map((section) => normalizeWhitespace(section)).filter(Boolean).join("\n\n").trim();
|
|
3674
|
+
}
|
|
3675
|
+
function parseDocxCoreMetadata(bytes) {
|
|
3676
|
+
try {
|
|
3677
|
+
const archive = unzipSync(new Uint8Array(bytes));
|
|
3678
|
+
const coreXml = archive["docProps/core.xml"];
|
|
3679
|
+
if (!coreXml) {
|
|
3680
|
+
return void 0;
|
|
3681
|
+
}
|
|
3682
|
+
const dom = new JSDOM(strFromU8(coreXml), { contentType: "text/xml" });
|
|
3683
|
+
const document = dom.window.document;
|
|
3684
|
+
const valuesByLocalName = /* @__PURE__ */ new Map();
|
|
3685
|
+
for (const node of Array.from(document.getElementsByTagName("*"))) {
|
|
3686
|
+
const localName = node.localName?.trim().toLowerCase();
|
|
3687
|
+
const text = normalizeWhitespace(node.textContent ?? "");
|
|
3688
|
+
if (!localName || !text || valuesByLocalName.has(localName)) {
|
|
3689
|
+
continue;
|
|
3690
|
+
}
|
|
3691
|
+
valuesByLocalName.set(localName, text);
|
|
3692
|
+
}
|
|
3693
|
+
const metadata = {};
|
|
3694
|
+
const mappings = [
|
|
3695
|
+
["title", "title"],
|
|
3696
|
+
["author", "creator"],
|
|
3697
|
+
["subject", "subject"],
|
|
3698
|
+
["description", "description"],
|
|
3699
|
+
["keywords", "keywords"],
|
|
3700
|
+
["last_modified_by", "lastmodifiedby"],
|
|
3701
|
+
["created", "created"],
|
|
3702
|
+
["modified", "modified"]
|
|
3703
|
+
];
|
|
3704
|
+
for (const [targetKey, sourceKey] of mappings) {
|
|
3705
|
+
const value = valuesByLocalName.get(sourceKey);
|
|
3706
|
+
if (value) {
|
|
3707
|
+
metadata[targetKey] = value;
|
|
3708
|
+
}
|
|
3709
|
+
}
|
|
3710
|
+
return Object.keys(metadata).length ? metadata : void 0;
|
|
3711
|
+
} catch {
|
|
3712
|
+
return void 0;
|
|
3713
|
+
}
|
|
3714
|
+
}
|
|
3504
3715
|
async function extractPdfText(input) {
|
|
3505
3716
|
try {
|
|
3506
3717
|
const pdfjs = await import("pdfjs-dist/legacy/build/pdf.mjs");
|
|
@@ -3548,6 +3759,35 @@ async function extractPdfText(input) {
|
|
|
3548
3759
|
};
|
|
3549
3760
|
}
|
|
3550
3761
|
}
|
|
3762
|
+
async function extractDocxText(input) {
|
|
3763
|
+
try {
|
|
3764
|
+
const mammoth = await import("mammoth");
|
|
3765
|
+
const result = await mammoth.extractRawText({
|
|
3766
|
+
buffer: input.bytes
|
|
3767
|
+
});
|
|
3768
|
+
const extractedText = normalizeDocumentText(result.value);
|
|
3769
|
+
const warnings = result.messages.map((message) => normalizeWhitespace(message.message)).filter(Boolean).map((message) => truncate(message, 240));
|
|
3770
|
+
const artifact = {
|
|
3771
|
+
...extractionMetadata("docx", input.mimeType, "docx_text"),
|
|
3772
|
+
metadata: parseDocxCoreMetadata(input.bytes),
|
|
3773
|
+
warnings: warnings.length ? warnings : void 0
|
|
3774
|
+
};
|
|
3775
|
+
if (!extractedText) {
|
|
3776
|
+
artifact.warnings = [...artifact.warnings ?? [], "DOCX text extraction completed but produced no extractable text."];
|
|
3777
|
+
}
|
|
3778
|
+
return {
|
|
3779
|
+
extractedText: extractedText || void 0,
|
|
3780
|
+
artifact
|
|
3781
|
+
};
|
|
3782
|
+
} catch (error) {
|
|
3783
|
+
return {
|
|
3784
|
+
artifact: {
|
|
3785
|
+
...extractionMetadata("docx", input.mimeType, "docx_text"),
|
|
3786
|
+
warnings: [`DOCX text extraction failed: ${error instanceof Error ? truncate(error.message, 240) : "unknown error"}`]
|
|
3787
|
+
}
|
|
3788
|
+
};
|
|
3789
|
+
}
|
|
3790
|
+
}
|
|
3551
3791
|
|
|
3552
3792
|
// src/logs.ts
|
|
3553
3793
|
import fs7 from "fs/promises";
|
|
@@ -3858,6 +4098,9 @@ function inferKind(mimeType, filePath) {
|
|
|
3858
4098
|
if (mimeType === "application/pdf" || filePath.toLowerCase().endsWith(".pdf")) {
|
|
3859
4099
|
return "pdf";
|
|
3860
4100
|
}
|
|
4101
|
+
if (mimeType === "application/vnd.openxmlformats-officedocument.wordprocessingml.document" || filePath.toLowerCase().endsWith(".docx")) {
|
|
4102
|
+
return "docx";
|
|
4103
|
+
}
|
|
3861
4104
|
if (mimeType.startsWith("image/")) {
|
|
3862
4105
|
return "image";
|
|
3863
4106
|
}
|
|
@@ -4036,7 +4279,7 @@ async function fetchResolvedText(url) {
|
|
|
4036
4279
|
};
|
|
4037
4280
|
}
|
|
4038
4281
|
function domTextFromHtml(html, baseUrl) {
|
|
4039
|
-
const dom = new
|
|
4282
|
+
const dom = new JSDOM2(`<body>${html}</body>`, { url: baseUrl });
|
|
4040
4283
|
return normalizeWhitespace(dom.window.document.body.textContent ?? "");
|
|
4041
4284
|
}
|
|
4042
4285
|
async function captureArxivMarkdown(input, options) {
|
|
@@ -4046,7 +4289,7 @@ async function captureArxivMarkdown(input, options) {
|
|
|
4046
4289
|
}
|
|
4047
4290
|
const normalizedUrl = `https://arxiv.org/abs/${arxivId}`;
|
|
4048
4291
|
const html = await fetchText(normalizedUrl);
|
|
4049
|
-
const dom = new
|
|
4292
|
+
const dom = new JSDOM2(html, { url: normalizedUrl });
|
|
4050
4293
|
const document = dom.window.document;
|
|
4051
4294
|
const metaTitle = document.querySelector('meta[name="citation_title"]')?.getAttribute("content")?.trim();
|
|
4052
4295
|
const headingTitle = document.querySelector("h1.title")?.textContent?.trim();
|
|
@@ -4155,7 +4398,7 @@ async function captureArticleMarkdown(rootDir, input, options, extra = { sourceT
|
|
|
4155
4398
|
if (!resolved.contentType.includes("html")) {
|
|
4156
4399
|
throw new Error(`Unsupported article content type: ${resolved.contentType}`);
|
|
4157
4400
|
}
|
|
4158
|
-
const dom = new
|
|
4401
|
+
const dom = new JSDOM2(resolved.text, { url: resolved.finalUrl });
|
|
4159
4402
|
const document = dom.window.document;
|
|
4160
4403
|
const canonicalHref = document.querySelector('link[rel="canonical"]')?.getAttribute("href")?.trim();
|
|
4161
4404
|
const canonicalUrl = canonicalHref ? normalizeOriginUrl(new URL(canonicalHref, resolved.finalUrl).toString()) : resolved.finalUrl;
|
|
@@ -4274,6 +4517,22 @@ function extractMarkdownReferences(content) {
|
|
|
4274
4517
|
}
|
|
4275
4518
|
return references;
|
|
4276
4519
|
}
|
|
4520
|
+
function extractHtmlLocalReferences(html, baseUrl) {
|
|
4521
|
+
const dom = new JSDOM2(html, { url: baseUrl });
|
|
4522
|
+
const document = dom.window.document;
|
|
4523
|
+
const references = [];
|
|
4524
|
+
for (const image of [...document.querySelectorAll("img[src]")]) {
|
|
4525
|
+
const src = image.getAttribute("src");
|
|
4526
|
+
if (!src) {
|
|
4527
|
+
continue;
|
|
4528
|
+
}
|
|
4529
|
+
const normalized = normalizeLocalReference(src);
|
|
4530
|
+
if (normalized) {
|
|
4531
|
+
references.push(normalized);
|
|
4532
|
+
}
|
|
4533
|
+
}
|
|
4534
|
+
return references;
|
|
4535
|
+
}
|
|
4277
4536
|
function normalizeRemoteReference(value, baseUrl) {
|
|
4278
4537
|
const trimmed = value.trim().replace(/^<|>$/g, "");
|
|
4279
4538
|
const [withoutTitle] = trimmed.split(/\s+(?=(?:[^"]*"[^"]*")*[^"]*$)/, 1);
|
|
@@ -4309,7 +4568,7 @@ function extractMarkdownImageReferences(content, baseUrl) {
|
|
|
4309
4568
|
return references;
|
|
4310
4569
|
}
|
|
4311
4570
|
async function convertHtmlToMarkdown(html, url) {
|
|
4312
|
-
const dom = new
|
|
4571
|
+
const dom = new JSDOM2(html, { url });
|
|
4313
4572
|
const article = new Readability(dom.window.document).parse();
|
|
4314
4573
|
const turndown = new TurndownService({ headingStyle: "atx", codeBlockStyle: "fenced" });
|
|
4315
4574
|
const body = article?.content ?? dom.window.document.body.innerHTML;
|
|
@@ -4512,7 +4771,7 @@ async function collectRemoteImageAttachments(assetUrls, options) {
|
|
|
4512
4771
|
return { attachments, skippedCount };
|
|
4513
4772
|
}
|
|
4514
4773
|
function extractHtmlImageReferences(html, baseUrl) {
|
|
4515
|
-
const dom = new
|
|
4774
|
+
const dom = new JSDOM2(html, { url: baseUrl });
|
|
4516
4775
|
const document = dom.window.document;
|
|
4517
4776
|
const references = [];
|
|
4518
4777
|
for (const image of [...document.querySelectorAll("img[src]")]) {
|
|
@@ -4528,7 +4787,7 @@ function extractHtmlImageReferences(html, baseUrl) {
|
|
|
4528
4787
|
return references;
|
|
4529
4788
|
}
|
|
4530
4789
|
function rewriteHtmlImageReferences(html, baseUrl, replacements) {
|
|
4531
|
-
const dom = new
|
|
4790
|
+
const dom = new JSDOM2(html, { url: baseUrl });
|
|
4532
4791
|
const document = dom.window.document;
|
|
4533
4792
|
for (const image of [...document.querySelectorAll("img[src]")]) {
|
|
4534
4793
|
const src = image.getAttribute("src");
|
|
@@ -4543,6 +4802,22 @@ function rewriteHtmlImageReferences(html, baseUrl, replacements) {
|
|
|
4543
4802
|
}
|
|
4544
4803
|
return dom.serialize();
|
|
4545
4804
|
}
|
|
4805
|
+
function rewriteHtmlLocalReferences(html, baseUrl, replacements) {
|
|
4806
|
+
const dom = new JSDOM2(html, { url: baseUrl });
|
|
4807
|
+
const document = dom.window.document;
|
|
4808
|
+
for (const image of [...document.querySelectorAll("img[src]")]) {
|
|
4809
|
+
const src = image.getAttribute("src");
|
|
4810
|
+
if (!src) {
|
|
4811
|
+
continue;
|
|
4812
|
+
}
|
|
4813
|
+
const normalized = normalizeLocalReference(src);
|
|
4814
|
+
const replacement = normalized ? replacements.get(normalized) : void 0;
|
|
4815
|
+
if (replacement) {
|
|
4816
|
+
image.setAttribute("src", replacement);
|
|
4817
|
+
}
|
|
4818
|
+
}
|
|
4819
|
+
return dom.serialize();
|
|
4820
|
+
}
|
|
4546
4821
|
function rewriteMarkdownImageReferences(content, baseUrl, replacements) {
|
|
4547
4822
|
return content.replace(/(!\[[^\]]*]\()([^)]+)(\))/g, (fullMatch, prefix, target, suffix) => {
|
|
4548
4823
|
const normalized = normalizeRemoteReference(target, baseUrl);
|
|
@@ -4683,7 +4958,7 @@ function preparedMatchesManifest(manifest, prepared, contentHash) {
|
|
|
4683
4958
|
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;
|
|
4684
4959
|
}
|
|
4685
4960
|
function shouldDeferWatchSemanticRefresh(sourceKind) {
|
|
4686
|
-
return sourceKind === "markdown" || sourceKind === "text" || sourceKind === "html" || sourceKind === "pdf" || sourceKind === "image";
|
|
4961
|
+
return sourceKind === "markdown" || sourceKind === "text" || sourceKind === "html" || sourceKind === "pdf" || sourceKind === "docx" || sourceKind === "image";
|
|
4687
4962
|
}
|
|
4688
4963
|
function pendingSemanticRefreshId(changeType, repoRoot, relativePath) {
|
|
4689
4964
|
return `pending:${changeType}:${sha256(`${toPosix(repoRoot)}:${relativePath}`).slice(0, 12)}`;
|
|
@@ -4947,6 +5222,12 @@ async function prepareFileInput(rootDir, absoluteInput, repoRoot, sourceClass) {
|
|
|
4947
5222
|
const extracted = await extractPdfText({ mimeType, bytes: payloadBytes });
|
|
4948
5223
|
extractedText = extracted.extractedText;
|
|
4949
5224
|
extractionArtifact = extracted.artifact;
|
|
5225
|
+
} else if (sourceKind === "docx") {
|
|
5226
|
+
title = path10.basename(absoluteInput, path10.extname(absoluteInput));
|
|
5227
|
+
const extracted = await extractDocxText({ mimeType, bytes: payloadBytes });
|
|
5228
|
+
title = extracted.artifact.metadata?.title?.trim() || title;
|
|
5229
|
+
extractedText = extracted.extractedText;
|
|
5230
|
+
extractionArtifact = extracted.artifact;
|
|
4950
5231
|
} else if (sourceKind === "image") {
|
|
4951
5232
|
title = path10.basename(absoluteInput, path10.extname(absoluteInput));
|
|
4952
5233
|
const extracted = await extractImageWithVision(rootDir, {
|
|
@@ -5064,6 +5345,11 @@ async function prepareUrlInput(rootDir, input, options) {
|
|
|
5064
5345
|
const extracted = await extractPdfText({ mimeType, bytes: payloadBytes });
|
|
5065
5346
|
extractedText = extracted.extractedText;
|
|
5066
5347
|
extractionArtifact = extracted.artifact;
|
|
5348
|
+
} else if (sourceKind === "docx") {
|
|
5349
|
+
const extracted = await extractDocxText({ mimeType, bytes: payloadBytes });
|
|
5350
|
+
title = extracted.artifact.metadata?.title?.trim() || title;
|
|
5351
|
+
extractedText = extracted.extractedText;
|
|
5352
|
+
extractionArtifact = extracted.artifact;
|
|
5067
5353
|
} else if (sourceKind === "image") {
|
|
5068
5354
|
const extracted = await extractImageWithVision(rootDir, {
|
|
5069
5355
|
title,
|
|
@@ -5097,11 +5383,11 @@ async function collectInboxAttachmentRefs(inputDir, files) {
|
|
|
5097
5383
|
for (const absolutePath of files) {
|
|
5098
5384
|
const mimeType = guessMimeType(absolutePath);
|
|
5099
5385
|
const sourceKind = inferKind(mimeType, absolutePath);
|
|
5100
|
-
if (sourceKind !== "markdown") {
|
|
5386
|
+
if (sourceKind !== "markdown" && sourceKind !== "html") {
|
|
5101
5387
|
continue;
|
|
5102
5388
|
}
|
|
5103
5389
|
const content = await fs9.readFile(absolutePath, "utf8");
|
|
5104
|
-
const refs = extractMarkdownReferences(content);
|
|
5390
|
+
const refs = sourceKind === "html" ? extractHtmlLocalReferences(content, pathToFileURL(absolutePath).toString()) : extractMarkdownReferences(content);
|
|
5105
5391
|
if (!refs.length) {
|
|
5106
5392
|
continue;
|
|
5107
5393
|
}
|
|
@@ -5179,8 +5465,50 @@ async function prepareInboxMarkdownInput(absolutePath, attachmentRefs) {
|
|
|
5179
5465
|
contentHash
|
|
5180
5466
|
};
|
|
5181
5467
|
}
|
|
5468
|
+
async function prepareInboxHtmlInput(absolutePath, attachmentRefs) {
|
|
5469
|
+
const originalBytes = await fs9.readFile(absolutePath);
|
|
5470
|
+
const originalHtml = originalBytes.toString("utf8");
|
|
5471
|
+
const initialConversion = await convertHtmlToMarkdown(originalHtml, pathToFileURL(absolutePath).toString());
|
|
5472
|
+
const attachments = [];
|
|
5473
|
+
for (const attachmentRef of attachmentRefs) {
|
|
5474
|
+
const bytes = await fs9.readFile(attachmentRef.absolutePath);
|
|
5475
|
+
attachments.push({
|
|
5476
|
+
relativePath: sanitizeAssetRelativePath(attachmentRef.relativeRef),
|
|
5477
|
+
mimeType: guessMimeType(attachmentRef.absolutePath),
|
|
5478
|
+
originalPath: toPosix(attachmentRef.absolutePath),
|
|
5479
|
+
bytes
|
|
5480
|
+
});
|
|
5481
|
+
}
|
|
5482
|
+
const contentHash = buildCompositeHash(originalBytes, attachments);
|
|
5483
|
+
const fallbackTitle = path10.basename(absolutePath, path10.extname(absolutePath));
|
|
5484
|
+
const title = initialConversion.title || fallbackTitle;
|
|
5485
|
+
const sourceId = `${slugify(title)}-${contentHash.slice(0, 8)}`;
|
|
5486
|
+
const replacements = new Map(
|
|
5487
|
+
attachmentRefs.map((attachmentRef) => [
|
|
5488
|
+
attachmentRef.relativeRef.replace(/\\/g, "/"),
|
|
5489
|
+
`../assets/${sourceId}/${sanitizeAssetRelativePath(attachmentRef.relativeRef)}`
|
|
5490
|
+
])
|
|
5491
|
+
);
|
|
5492
|
+
const rewrittenHtml = rewriteHtmlLocalReferences(originalHtml, pathToFileURL(absolutePath).toString(), replacements);
|
|
5493
|
+
const converted = rewrittenHtml === originalHtml ? initialConversion : await convertHtmlToMarkdown(rewrittenHtml, pathToFileURL(absolutePath).toString());
|
|
5494
|
+
const extractionArtifact = createHtmlReadabilityExtractionArtifact("html", "text/html");
|
|
5495
|
+
return {
|
|
5496
|
+
title: converted.title || title,
|
|
5497
|
+
originType: "file",
|
|
5498
|
+
sourceKind: "html",
|
|
5499
|
+
originalPath: toPosix(absolutePath),
|
|
5500
|
+
mimeType: "text/html",
|
|
5501
|
+
storedExtension: path10.extname(absolutePath) || ".html",
|
|
5502
|
+
payloadBytes: Buffer.from(rewrittenHtml, "utf8"),
|
|
5503
|
+
extractedText: converted.markdown,
|
|
5504
|
+
extractionArtifact,
|
|
5505
|
+
extractionHash: buildExtractionHash(converted.markdown, extractionArtifact),
|
|
5506
|
+
attachments,
|
|
5507
|
+
contentHash
|
|
5508
|
+
};
|
|
5509
|
+
}
|
|
5182
5510
|
function isSupportedInboxKind(sourceKind) {
|
|
5183
|
-
return ["markdown", "text", "html", "pdf", "image"].includes(sourceKind);
|
|
5511
|
+
return ["markdown", "text", "html", "pdf", "docx", "image"].includes(sourceKind);
|
|
5184
5512
|
}
|
|
5185
5513
|
async function ingestInput(rootDir, input, options) {
|
|
5186
5514
|
const { paths } = await initWorkspace(rootDir);
|
|
@@ -5340,7 +5668,7 @@ async function importInbox(rootDir, inputDir) {
|
|
|
5340
5668
|
skipped.push({ path: toPosix(path10.relative(rootDir, absolutePath)), reason: `unsupported_kind:${sourceKind}` });
|
|
5341
5669
|
continue;
|
|
5342
5670
|
}
|
|
5343
|
-
const prepared = sourceKind === "markdown" && refsBySource.has(absolutePath) ? await prepareInboxMarkdownInput(absolutePath, refsBySource.get(absolutePath) ?? []) : await prepareFileInput(rootDir, absolutePath);
|
|
5671
|
+
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);
|
|
5344
5672
|
const result = await persistPreparedInput(rootDir, prepared, paths);
|
|
5345
5673
|
if (!result.isNew) {
|
|
5346
5674
|
skipped.push({ path: toPosix(path10.relative(rootDir, absolutePath)), reason: "duplicate_content" });
|
|
@@ -6614,8 +6942,14 @@ async function loadPageContents(rootDir, graph) {
|
|
|
6614
6942
|
await Promise.all(
|
|
6615
6943
|
graph.pages.map(async (page) => {
|
|
6616
6944
|
const absolutePath = path16.join(paths.wikiDir, page.path);
|
|
6617
|
-
const content = await fs12.readFile(absolutePath, "utf8").catch(() =>
|
|
6618
|
-
|
|
6945
|
+
const content = await fs12.readFile(absolutePath, "utf8").catch(() => {
|
|
6946
|
+
process.stderr.write(`[swarmvault] Warning: could not read page ${page.path} for embedding
|
|
6947
|
+
`);
|
|
6948
|
+
return "";
|
|
6949
|
+
});
|
|
6950
|
+
if (content) {
|
|
6951
|
+
contents.set(page.id, content);
|
|
6952
|
+
}
|
|
6619
6953
|
})
|
|
6620
6954
|
);
|
|
6621
6955
|
return contents;
|
|
@@ -9713,7 +10047,7 @@ async function resolveImageGenerationProvider(rootDir) {
|
|
|
9713
10047
|
if (!providerConfig) {
|
|
9714
10048
|
throw new Error(`No provider configured with id "${preferredProviderId}" for task "imageProvider".`);
|
|
9715
10049
|
}
|
|
9716
|
-
const { createProvider: createProvider2 } = await import("./registry-
|
|
10050
|
+
const { createProvider: createProvider2 } = await import("./registry-5SYH3Y3U.js");
|
|
9717
10051
|
return createProvider2(preferredProviderId, providerConfig, rootDir);
|
|
9718
10052
|
}
|
|
9719
10053
|
async function generateOutputArtifacts(rootDir, input) {
|
|
@@ -10274,8 +10608,64 @@ function deriveGraphMetrics(nodes, edges) {
|
|
|
10274
10608
|
function resetGraphNodeMetrics(nodes) {
|
|
10275
10609
|
return nodes.map(({ communityId: _communityId, degree: _degree, bridgeScore: _bridgeScore, isGodNode: _isGodNode, ...node }) => node);
|
|
10276
10610
|
}
|
|
10611
|
+
function manifestRepoPath(manifest) {
|
|
10612
|
+
return toPosix(manifest.repoRelativePath ?? path20.basename(manifest.originalPath ?? manifest.storedPath));
|
|
10613
|
+
}
|
|
10614
|
+
function goPackageScopeKey(manifest, analysis) {
|
|
10615
|
+
if (analysis.code?.language !== "go") {
|
|
10616
|
+
return null;
|
|
10617
|
+
}
|
|
10618
|
+
const packageName = analysis.code.namespace?.trim();
|
|
10619
|
+
if (!packageName) {
|
|
10620
|
+
return null;
|
|
10621
|
+
}
|
|
10622
|
+
return `${packageName}:${path20.posix.dirname(manifestRepoPath(manifest))}`;
|
|
10623
|
+
}
|
|
10624
|
+
function buildGoPackageSymbolLookups(analyses, manifestsById) {
|
|
10625
|
+
const lookups = /* @__PURE__ */ new Map();
|
|
10626
|
+
for (const analysis of analyses) {
|
|
10627
|
+
if (analysis.code?.language !== "go") {
|
|
10628
|
+
continue;
|
|
10629
|
+
}
|
|
10630
|
+
const manifest = manifestsById.get(analysis.sourceId);
|
|
10631
|
+
if (!manifest) {
|
|
10632
|
+
continue;
|
|
10633
|
+
}
|
|
10634
|
+
const scopeKey = goPackageScopeKey(manifest, analysis);
|
|
10635
|
+
if (!scopeKey) {
|
|
10636
|
+
continue;
|
|
10637
|
+
}
|
|
10638
|
+
const current = lookups.get(scopeKey) ?? {
|
|
10639
|
+
byName: /* @__PURE__ */ new Map(),
|
|
10640
|
+
methodIdsByShortName: /* @__PURE__ */ new Map()
|
|
10641
|
+
};
|
|
10642
|
+
for (const symbol of analysis.code.symbols) {
|
|
10643
|
+
current.byName.set(symbol.name, symbol.id);
|
|
10644
|
+
const separator = symbol.name.lastIndexOf(".");
|
|
10645
|
+
if (separator > 0) {
|
|
10646
|
+
const shortName = symbol.name.slice(separator + 1);
|
|
10647
|
+
const matches = current.methodIdsByShortName.get(shortName) ?? /* @__PURE__ */ new Set();
|
|
10648
|
+
matches.add(symbol.id);
|
|
10649
|
+
current.methodIdsByShortName.set(shortName, matches);
|
|
10650
|
+
}
|
|
10651
|
+
}
|
|
10652
|
+
lookups.set(scopeKey, current);
|
|
10653
|
+
}
|
|
10654
|
+
return new Map(
|
|
10655
|
+
[...lookups.entries()].map(([scopeKey, value]) => [
|
|
10656
|
+
scopeKey,
|
|
10657
|
+
{
|
|
10658
|
+
byName: value.byName,
|
|
10659
|
+
uniqueMethodIdsByShortName: new Map(
|
|
10660
|
+
[...value.methodIdsByShortName.entries()].filter(([, ids]) => ids.size === 1).map(([shortName, ids]) => [shortName, [...ids][0]])
|
|
10661
|
+
)
|
|
10662
|
+
}
|
|
10663
|
+
])
|
|
10664
|
+
);
|
|
10665
|
+
}
|
|
10277
10666
|
function buildGraph(manifests, analyses, pages, sourceProjects, _codeIndex) {
|
|
10278
10667
|
const manifestsById = new Map(manifests.map((manifest) => [manifest.sourceId, manifest]));
|
|
10668
|
+
const goPackageSymbolLookups = buildGoPackageSymbolLookups(analyses, manifestsById);
|
|
10279
10669
|
const sourceNodes = manifests.map((manifest) => ({
|
|
10280
10670
|
id: `source:${manifest.sourceId}`,
|
|
10281
10671
|
type: "source",
|
|
@@ -10422,6 +10812,10 @@ function buildGraph(manifests, analyses, pages, sourceProjects, _codeIndex) {
|
|
|
10422
10812
|
}
|
|
10423
10813
|
}
|
|
10424
10814
|
const symbolIdsByName = new Map(analysis.code.symbols.map((symbol) => [symbol.name, symbol.id]));
|
|
10815
|
+
const goPackageLookup = analysis.code.language === "go" ? goPackageSymbolLookups.get(goPackageScopeKey(manifest, analysis) ?? "") : void 0;
|
|
10816
|
+
const localSymbolIdsByName = goPackageLookup?.byName ?? symbolIdsByName;
|
|
10817
|
+
const localGoMethodIdsByShortName = goPackageLookup?.uniqueMethodIdsByShortName ?? /* @__PURE__ */ new Map();
|
|
10818
|
+
const resolveLocalSymbolId = (targetName) => localSymbolIdsByName.get(targetName) ?? (analysis.code?.language === "go" ? localGoMethodIdsByShortName.get(targetName) : void 0);
|
|
10425
10819
|
for (const rationale of analysis.rationales) {
|
|
10426
10820
|
const targetSymbolId = rationale.symbolName ? symbolIdsByName.get(rationale.symbolName) : void 0;
|
|
10427
10821
|
const targetId = targetSymbolId ?? moduleId;
|
|
@@ -10474,9 +10868,31 @@ function buildGraph(manifests, analyses, pages, sourceProjects, _codeIndex) {
|
|
|
10474
10868
|
}
|
|
10475
10869
|
}
|
|
10476
10870
|
}
|
|
10871
|
+
if (analysis.code.language === "go") {
|
|
10872
|
+
for (const symbol of analysis.code.symbols) {
|
|
10873
|
+
const separator = symbol.name.lastIndexOf(".");
|
|
10874
|
+
if (separator <= 0) {
|
|
10875
|
+
continue;
|
|
10876
|
+
}
|
|
10877
|
+
const receiverTypeId = localSymbolIdsByName.get(symbol.name.slice(0, separator));
|
|
10878
|
+
if (!receiverTypeId || receiverTypeId === symbol.id) {
|
|
10879
|
+
continue;
|
|
10880
|
+
}
|
|
10881
|
+
pushEdge({
|
|
10882
|
+
id: `${receiverTypeId}->${symbol.id}:defines:receiver`,
|
|
10883
|
+
source: receiverTypeId,
|
|
10884
|
+
target: symbol.id,
|
|
10885
|
+
relation: "defines",
|
|
10886
|
+
status: "extracted",
|
|
10887
|
+
evidenceClass: "extracted",
|
|
10888
|
+
confidence: 1,
|
|
10889
|
+
provenance: [analysis.sourceId]
|
|
10890
|
+
});
|
|
10891
|
+
}
|
|
10892
|
+
}
|
|
10477
10893
|
for (const symbol of analysis.code.symbols) {
|
|
10478
10894
|
for (const targetName of symbol.calls) {
|
|
10479
|
-
const targetId =
|
|
10895
|
+
const targetId = resolveLocalSymbolId(targetName);
|
|
10480
10896
|
if (!targetId || targetId === symbol.id) {
|
|
10481
10897
|
continue;
|
|
10482
10898
|
}
|
|
@@ -10492,7 +10908,7 @@ function buildGraph(manifests, analyses, pages, sourceProjects, _codeIndex) {
|
|
|
10492
10908
|
});
|
|
10493
10909
|
}
|
|
10494
10910
|
for (const targetName of symbol.extends) {
|
|
10495
|
-
const targetId =
|
|
10911
|
+
const targetId = resolveLocalSymbolId(targetName) ?? importedSymbolIdsByName.get(targetName);
|
|
10496
10912
|
if (!targetId) {
|
|
10497
10913
|
continue;
|
|
10498
10914
|
}
|
|
@@ -10508,7 +10924,7 @@ function buildGraph(manifests, analyses, pages, sourceProjects, _codeIndex) {
|
|
|
10508
10924
|
});
|
|
10509
10925
|
}
|
|
10510
10926
|
for (const targetName of symbol.implements) {
|
|
10511
|
-
const targetId =
|
|
10927
|
+
const targetId = resolveLocalSymbolId(targetName) ?? importedSymbolIdsByName.get(targetName);
|
|
10512
10928
|
if (!targetId) {
|
|
10513
10929
|
continue;
|
|
10514
10930
|
}
|
|
@@ -12988,7 +13404,7 @@ async function bootstrapDemo(rootDir, input) {
|
|
|
12988
13404
|
}
|
|
12989
13405
|
|
|
12990
13406
|
// src/mcp.ts
|
|
12991
|
-
var SERVER_VERSION = "0.1.
|
|
13407
|
+
var SERVER_VERSION = "0.1.29";
|
|
12992
13408
|
async function createMcpServer(rootDir) {
|
|
12993
13409
|
const server = new McpServer({
|
|
12994
13410
|
name: "swarmvault",
|
|
@@ -13922,51 +14338,33 @@ async function watchVault(rootDir, options = {}) {
|
|
|
13922
14338
|
}
|
|
13923
14339
|
} finally {
|
|
13924
14340
|
const finishedAt = /* @__PURE__ */ new Date();
|
|
13925
|
-
|
|
13926
|
-
|
|
13927
|
-
|
|
13928
|
-
|
|
13929
|
-
|
|
13930
|
-
|
|
13931
|
-
|
|
13932
|
-
|
|
13933
|
-
|
|
13934
|
-
|
|
13935
|
-
|
|
13936
|
-
|
|
13937
|
-
|
|
13938
|
-
|
|
13939
|
-
|
|
13940
|
-
|
|
13941
|
-
|
|
13942
|
-
|
|
13943
|
-
|
|
13944
|
-
|
|
13945
|
-
|
|
13946
|
-
|
|
13947
|
-
|
|
13948
|
-
|
|
13949
|
-
|
|
13950
|
-
|
|
13951
|
-
|
|
13952
|
-
importedCount: importedCount + repoImportedCount + repoUpdatedCount,
|
|
13953
|
-
scannedCount: scannedCount + repoScannedCount,
|
|
13954
|
-
attachmentCount,
|
|
13955
|
-
changedPages,
|
|
13956
|
-
repoImportedCount,
|
|
13957
|
-
repoUpdatedCount,
|
|
13958
|
-
repoRemovedCount,
|
|
13959
|
-
repoScannedCount,
|
|
13960
|
-
pendingSemanticRefreshCount,
|
|
13961
|
-
pendingSemanticRefreshPaths,
|
|
13962
|
-
lintFindingCount,
|
|
13963
|
-
success,
|
|
13964
|
-
error
|
|
13965
|
-
});
|
|
13966
|
-
await writeWatchStatusArtifact(rootDir, {
|
|
13967
|
-
generatedAt: finishedAt.toISOString(),
|
|
13968
|
-
watchedRepoRoots,
|
|
13969
|
-
lastRun: {
|
|
14341
|
+
try {
|
|
14342
|
+
await recordSession(rootDir, {
|
|
14343
|
+
operation: "watch",
|
|
14344
|
+
title: `Watch cycle for ${paths.inboxDir}${options.repo ? " and tracked repos" : ""}`,
|
|
14345
|
+
startedAt: startedAt.toISOString(),
|
|
14346
|
+
finishedAt: finishedAt.toISOString(),
|
|
14347
|
+
success,
|
|
14348
|
+
error,
|
|
14349
|
+
changedPages,
|
|
14350
|
+
lintFindingCount,
|
|
14351
|
+
lines: [
|
|
14352
|
+
`reasons=${runReasons.join(",") || "none"}`,
|
|
14353
|
+
`imported=${importedCount}`,
|
|
14354
|
+
`scanned=${scannedCount}`,
|
|
14355
|
+
`attachments=${attachmentCount}`,
|
|
14356
|
+
`repo_scanned=${repoScannedCount}`,
|
|
14357
|
+
`repo_imported=${repoImportedCount}`,
|
|
14358
|
+
`repo_updated=${repoUpdatedCount}`,
|
|
14359
|
+
`repo_removed=${repoRemovedCount}`,
|
|
14360
|
+
`lint=${lintFindingCount ?? 0}`
|
|
14361
|
+
]
|
|
14362
|
+
});
|
|
14363
|
+
} catch {
|
|
14364
|
+
process3.stderr.write("[swarmvault watch] Failed to record session log.\n");
|
|
14365
|
+
}
|
|
14366
|
+
try {
|
|
14367
|
+
await appendWatchRun(rootDir, {
|
|
13970
14368
|
startedAt: startedAt.toISOString(),
|
|
13971
14369
|
finishedAt: finishedAt.toISOString(),
|
|
13972
14370
|
durationMs: finishedAt.getTime() - startedAt.getTime(),
|
|
@@ -13985,9 +14383,39 @@ async function watchVault(rootDir, options = {}) {
|
|
|
13985
14383
|
lintFindingCount,
|
|
13986
14384
|
success,
|
|
13987
14385
|
error
|
|
13988
|
-
}
|
|
13989
|
-
|
|
13990
|
-
|
|
14386
|
+
});
|
|
14387
|
+
} catch {
|
|
14388
|
+
process3.stderr.write("[swarmvault watch] Failed to append watch run.\n");
|
|
14389
|
+
}
|
|
14390
|
+
try {
|
|
14391
|
+
await writeWatchStatusArtifact(rootDir, {
|
|
14392
|
+
generatedAt: finishedAt.toISOString(),
|
|
14393
|
+
watchedRepoRoots,
|
|
14394
|
+
lastRun: {
|
|
14395
|
+
startedAt: startedAt.toISOString(),
|
|
14396
|
+
finishedAt: finishedAt.toISOString(),
|
|
14397
|
+
durationMs: finishedAt.getTime() - startedAt.getTime(),
|
|
14398
|
+
inputDir: paths.inboxDir,
|
|
14399
|
+
reasons: runReasons,
|
|
14400
|
+
importedCount: importedCount + repoImportedCount + repoUpdatedCount,
|
|
14401
|
+
scannedCount: scannedCount + repoScannedCount,
|
|
14402
|
+
attachmentCount,
|
|
14403
|
+
changedPages,
|
|
14404
|
+
repoImportedCount,
|
|
14405
|
+
repoUpdatedCount,
|
|
14406
|
+
repoRemovedCount,
|
|
14407
|
+
repoScannedCount,
|
|
14408
|
+
pendingSemanticRefreshCount,
|
|
14409
|
+
pendingSemanticRefreshPaths,
|
|
14410
|
+
lintFindingCount,
|
|
14411
|
+
success,
|
|
14412
|
+
error
|
|
14413
|
+
},
|
|
14414
|
+
pendingSemanticRefresh: await readPendingSemanticRefresh(rootDir)
|
|
14415
|
+
});
|
|
14416
|
+
} catch {
|
|
14417
|
+
process3.stderr.write("[swarmvault watch] Failed to write watch status artifact.\n");
|
|
14418
|
+
}
|
|
13991
14419
|
running = false;
|
|
13992
14420
|
if (pending && !closed) {
|
|
13993
14421
|
schedule("queued");
|