@swarmvaultai/engine 0.6.6 → 0.6.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-5Q4IV4O3.js +1336 -0
- package/dist/hooks/claude.js +146 -0
- package/dist/hooks/copilot.js +141 -0
- package/dist/hooks/gemini.js +142 -0
- package/dist/hooks/opencode.js +50 -0
- package/dist/index.d.ts +0 -1
- package/dist/index.js +1013 -753
- package/dist/registry-W6ZFRI73.js +12 -0
- package/package.json +4 -2
package/dist/index.js
CHANGED
|
@@ -22,13 +22,44 @@ import {
|
|
|
22
22
|
uniqueBy,
|
|
23
23
|
writeFileIfChanged,
|
|
24
24
|
writeJsonFile
|
|
25
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-5Q4IV4O3.js";
|
|
26
26
|
|
|
27
27
|
// src/agents.ts
|
|
28
28
|
import crypto from "crypto";
|
|
29
29
|
import fs from "fs/promises";
|
|
30
30
|
import path from "path";
|
|
31
|
+
import { fileURLToPath } from "url";
|
|
31
32
|
import YAML from "yaml";
|
|
33
|
+
function resolveHooksDir() {
|
|
34
|
+
const moduleUrl = import.meta.url;
|
|
35
|
+
const modulePath = fileURLToPath(moduleUrl);
|
|
36
|
+
const moduleDir = path.dirname(modulePath);
|
|
37
|
+
if (moduleDir.endsWith(`${path.sep}dist`)) {
|
|
38
|
+
return path.join(moduleDir, "hooks");
|
|
39
|
+
}
|
|
40
|
+
if (moduleDir.endsWith(`${path.sep}src`)) {
|
|
41
|
+
return path.resolve(moduleDir, "..", "dist", "hooks");
|
|
42
|
+
}
|
|
43
|
+
return path.resolve(moduleDir, "hooks");
|
|
44
|
+
}
|
|
45
|
+
var BUILT_HOOKS_DIR = resolveHooksDir();
|
|
46
|
+
var hookContentCache = /* @__PURE__ */ new Map();
|
|
47
|
+
async function readBuiltHook(hookFile) {
|
|
48
|
+
const cached = hookContentCache.get(hookFile);
|
|
49
|
+
if (cached !== void 0) {
|
|
50
|
+
return cached;
|
|
51
|
+
}
|
|
52
|
+
const hookPath2 = path.join(BUILT_HOOKS_DIR, hookFile);
|
|
53
|
+
try {
|
|
54
|
+
const content = await fs.readFile(hookPath2, "utf8");
|
|
55
|
+
hookContentCache.set(hookFile, content);
|
|
56
|
+
return content;
|
|
57
|
+
} catch (error) {
|
|
58
|
+
throw new Error(
|
|
59
|
+
`SwarmVault hook bundle not found at ${hookPath2}. Run 'pnpm --filter @swarmvaultai/engine build' so the hook scripts are emitted to dist/hooks/. Underlying error: ${error instanceof Error ? error.message : String(error)}`
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
32
63
|
var managedStart = "<!-- swarmvault:managed:start -->";
|
|
33
64
|
var managedEnd = "<!-- swarmvault:managed:end -->";
|
|
34
65
|
var legacyManagedStart = "<!-- vault:managed:start -->";
|
|
@@ -64,6 +95,13 @@ function buildManagedBlock(target) {
|
|
|
64
95
|
""
|
|
65
96
|
].join("\n");
|
|
66
97
|
}
|
|
98
|
+
function buildCursorRule() {
|
|
99
|
+
const frontmatter = YAML.stringify({
|
|
100
|
+
description: "SwarmVault graph-first repository instructions.",
|
|
101
|
+
alwaysApply: true
|
|
102
|
+
}).trimEnd();
|
|
103
|
+
return ["---", frontmatter, "---", "", buildManagedBlock("cursor").trimEnd(), ""].join("\n");
|
|
104
|
+
}
|
|
67
105
|
function supportsAgentHook(agent) {
|
|
68
106
|
return agent === "claude" || agent === "opencode" || agent === "gemini" || agent === "copilot";
|
|
69
107
|
}
|
|
@@ -181,7 +219,7 @@ async function readJsonWithWarnings(filePath, fallback, label) {
|
|
|
181
219
|
async function installClaudeHook(rootDir) {
|
|
182
220
|
const settingsPath = path.join(rootDir, ".claude", "settings.json");
|
|
183
221
|
const scriptPath = path.join(rootDir, ".claude", "hooks", "swarmvault-graph-first.js");
|
|
184
|
-
await writeOwnedFile(scriptPath,
|
|
222
|
+
await writeOwnedFile(scriptPath, await readBuiltHook("claude.js"), true);
|
|
185
223
|
await ensureDir(path.dirname(settingsPath));
|
|
186
224
|
const { data: settings, warnings } = await readJsonWithWarnings(settingsPath, {}, ".claude/settings.json");
|
|
187
225
|
if (warnings.length > 0 && await fileExists(settingsPath)) {
|
|
@@ -211,361 +249,10 @@ async function installClaudeHook(rootDir) {
|
|
|
211
249
|
`, "utf8");
|
|
212
250
|
return { path: settingsPath, warnings: [] };
|
|
213
251
|
}
|
|
214
|
-
function buildClaudeHookScript() {
|
|
215
|
-
return `#!/usr/bin/env node
|
|
216
|
-
import crypto from "node:crypto";
|
|
217
|
-
import fs from "node:fs/promises";
|
|
218
|
-
import os from "node:os";
|
|
219
|
-
import path from "node:path";
|
|
220
|
-
|
|
221
|
-
${markerStateSnippet("claude").trim()}
|
|
222
|
-
|
|
223
|
-
async function readInput() {
|
|
224
|
-
let body = "";
|
|
225
|
-
for await (const chunk of process.stdin) {
|
|
226
|
-
body += chunk;
|
|
227
|
-
}
|
|
228
|
-
if (!body.trim()) {
|
|
229
|
-
return {};
|
|
230
|
-
}
|
|
231
|
-
try {
|
|
232
|
-
return JSON.parse(body);
|
|
233
|
-
} catch {
|
|
234
|
-
return {};
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
function emit(value) {
|
|
239
|
-
process.stdout.write(\`\${JSON.stringify(value)}\\n\`);
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
const mode = process.argv[2] ?? "";
|
|
243
|
-
const input = await readInput();
|
|
244
|
-
const cwd = resolveInputCwd(input);
|
|
245
|
-
const reportNote = "SwarmVault graph report exists at wiki/graph/report.md. Read it before broad grep/glob searching.";
|
|
246
|
-
|
|
247
|
-
if (!(await hasReport(cwd))) {
|
|
248
|
-
emit({});
|
|
249
|
-
process.exit(0);
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
if (mode === "session-start") {
|
|
253
|
-
await resetSession(cwd);
|
|
254
|
-
emit({
|
|
255
|
-
hookSpecificOutput: {
|
|
256
|
-
hookEventName: "SessionStart",
|
|
257
|
-
additionalContext: reportNote
|
|
258
|
-
}
|
|
259
|
-
});
|
|
260
|
-
process.exit(0);
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
const toolName = resolveToolName(input);
|
|
264
|
-
if (collectCandidatePaths(input).some((value) => isReportPath(value, cwd))) {
|
|
265
|
-
await markReportRead(cwd);
|
|
266
|
-
emit({});
|
|
267
|
-
process.exit(0);
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
if (isBroadSearchTool(toolName) && !(await hasSeenReport(cwd))) {
|
|
271
|
-
emit({
|
|
272
|
-
hookSpecificOutput: {
|
|
273
|
-
hookEventName: "PreToolUse",
|
|
274
|
-
additionalContext: reportNote
|
|
275
|
-
}
|
|
276
|
-
});
|
|
277
|
-
process.exit(0);
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
emit({});
|
|
281
|
-
`;
|
|
282
|
-
}
|
|
283
|
-
function markerStateSnippet(agentKey) {
|
|
284
|
-
return `
|
|
285
|
-
function markerState(cwd) {
|
|
286
|
-
const hash = crypto.createHash("sha256").update(cwd).digest("hex");
|
|
287
|
-
const dir = path.join(os.tmpdir(), "swarmvault-agent-hooks", "${agentKey}", hash);
|
|
288
|
-
return {
|
|
289
|
-
dir,
|
|
290
|
-
markerPath: path.join(dir, "report-read")
|
|
291
|
-
};
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
function isReportPath(value, cwd) {
|
|
295
|
-
if (typeof value !== "string" || value.length === 0) {
|
|
296
|
-
return false;
|
|
297
|
-
}
|
|
298
|
-
const reportSuffix = path.join("wiki", "graph", "report.md");
|
|
299
|
-
const normalized = value.replaceAll("\\\\", "/");
|
|
300
|
-
const reportNormalized = reportSuffix.replaceAll("\\\\", "/");
|
|
301
|
-
if (normalized.endsWith(reportNormalized)) {
|
|
302
|
-
return true;
|
|
303
|
-
}
|
|
304
|
-
return path.resolve(cwd, value) === path.resolve(cwd, reportSuffix);
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
function collectCandidatePaths(node, acc = []) {
|
|
308
|
-
if (typeof node === "string") {
|
|
309
|
-
acc.push(node);
|
|
310
|
-
return acc;
|
|
311
|
-
}
|
|
312
|
-
if (!node || typeof node !== "object") {
|
|
313
|
-
return acc;
|
|
314
|
-
}
|
|
315
|
-
if (Array.isArray(node)) {
|
|
316
|
-
for (const item of node) {
|
|
317
|
-
collectCandidatePaths(item, acc);
|
|
318
|
-
}
|
|
319
|
-
return acc;
|
|
320
|
-
}
|
|
321
|
-
for (const [key, value] of Object.entries(node)) {
|
|
322
|
-
if (["path", "filePath", "file_path", "paths", "target", "targets"].includes(key)) {
|
|
323
|
-
collectCandidatePaths(value, acc);
|
|
324
|
-
continue;
|
|
325
|
-
}
|
|
326
|
-
collectCandidatePaths(value, acc);
|
|
327
|
-
}
|
|
328
|
-
return acc;
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
function resolveInputCwd(input) {
|
|
332
|
-
return path.resolve(
|
|
333
|
-
input?.cwd ??
|
|
334
|
-
input?.directory ??
|
|
335
|
-
input?.workspace?.cwd ??
|
|
336
|
-
input?.toolInput?.cwd ??
|
|
337
|
-
process.cwd()
|
|
338
|
-
);
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
function resolveToolName(input) {
|
|
342
|
-
return String(input?.toolName ?? input?.tool_name ?? input?.tool?.name ?? input?.name ?? "");
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
async function hasReport(cwd) {
|
|
346
|
-
try {
|
|
347
|
-
await fs.access(path.join(cwd, "wiki", "graph", "report.md"));
|
|
348
|
-
return true;
|
|
349
|
-
} catch {
|
|
350
|
-
return false;
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
async function markReportRead(cwd) {
|
|
355
|
-
const state = markerState(cwd);
|
|
356
|
-
await fs.mkdir(state.dir, { recursive: true });
|
|
357
|
-
await fs.writeFile(state.markerPath, "seen\\n", "utf8");
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
async function hasSeenReport(cwd) {
|
|
361
|
-
const state = markerState(cwd);
|
|
362
|
-
try {
|
|
363
|
-
await fs.access(state.markerPath);
|
|
364
|
-
return true;
|
|
365
|
-
} catch {
|
|
366
|
-
return false;
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
async function resetSession(cwd) {
|
|
371
|
-
const state = markerState(cwd);
|
|
372
|
-
await fs.rm(state.dir, { recursive: true, force: true });
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
function isBroadSearchTool(toolName) {
|
|
376
|
-
return /grep|glob|search|find/i.test(toolName);
|
|
377
|
-
}
|
|
378
|
-
`;
|
|
379
|
-
}
|
|
380
|
-
function buildGeminiHookScript() {
|
|
381
|
-
return `#!/usr/bin/env node
|
|
382
|
-
import crypto from "node:crypto";
|
|
383
|
-
import fs from "node:fs/promises";
|
|
384
|
-
import os from "node:os";
|
|
385
|
-
import path from "node:path";
|
|
386
|
-
|
|
387
|
-
${markerStateSnippet("gemini").trim()}
|
|
388
|
-
|
|
389
|
-
async function readInput() {
|
|
390
|
-
let body = "";
|
|
391
|
-
for await (const chunk of process.stdin) {
|
|
392
|
-
body += chunk;
|
|
393
|
-
}
|
|
394
|
-
if (!body.trim()) {
|
|
395
|
-
return {};
|
|
396
|
-
}
|
|
397
|
-
try {
|
|
398
|
-
return JSON.parse(body);
|
|
399
|
-
} catch {
|
|
400
|
-
return {};
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
function emit(value) {
|
|
405
|
-
process.stdout.write(\`\${JSON.stringify(value)}\\n\`);
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
const mode = process.argv[2] ?? "";
|
|
409
|
-
const input = await readInput();
|
|
410
|
-
const cwd = resolveInputCwd(input);
|
|
411
|
-
const reportNote = "SwarmVault graph report exists at wiki/graph/report.md. Read it before broad grep/glob searching.";
|
|
412
|
-
|
|
413
|
-
if (!(await hasReport(cwd))) {
|
|
414
|
-
emit({});
|
|
415
|
-
process.exit(0);
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
if (mode === "session-start") {
|
|
419
|
-
await resetSession(cwd);
|
|
420
|
-
emit({
|
|
421
|
-
systemMessage: reportNote,
|
|
422
|
-
hookSpecificOutput: {
|
|
423
|
-
hookEventName: "SessionStart",
|
|
424
|
-
additionalContext: "SwarmVault graph report: wiki/graph/report.md"
|
|
425
|
-
}
|
|
426
|
-
});
|
|
427
|
-
process.exit(0);
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
const toolName = resolveToolName(input);
|
|
431
|
-
if (collectCandidatePaths(input).some((value) => isReportPath(value, cwd))) {
|
|
432
|
-
await markReportRead(cwd);
|
|
433
|
-
emit({});
|
|
434
|
-
process.exit(0);
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
if (isBroadSearchTool(toolName) && !(await hasSeenReport(cwd))) {
|
|
438
|
-
emit({ systemMessage: reportNote });
|
|
439
|
-
process.exit(0);
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
emit({});
|
|
443
|
-
`;
|
|
444
|
-
}
|
|
445
|
-
function buildCopilotHookScript() {
|
|
446
|
-
return `#!/usr/bin/env node
|
|
447
|
-
import crypto from "node:crypto";
|
|
448
|
-
import fs from "node:fs/promises";
|
|
449
|
-
import os from "node:os";
|
|
450
|
-
import path from "node:path";
|
|
451
|
-
|
|
452
|
-
${markerStateSnippet("copilot").trim()}
|
|
453
|
-
|
|
454
|
-
async function readInput() {
|
|
455
|
-
let body = "";
|
|
456
|
-
for await (const chunk of process.stdin) {
|
|
457
|
-
body += chunk;
|
|
458
|
-
}
|
|
459
|
-
if (!body.trim()) {
|
|
460
|
-
return {};
|
|
461
|
-
}
|
|
462
|
-
try {
|
|
463
|
-
return JSON.parse(body);
|
|
464
|
-
} catch {
|
|
465
|
-
return {};
|
|
466
|
-
}
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
function emit(value) {
|
|
470
|
-
if (value !== undefined) {
|
|
471
|
-
process.stdout.write(\`\${JSON.stringify(value)}\\n\`);
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
const mode = process.argv[2] ?? "";
|
|
476
|
-
const input = await readInput();
|
|
477
|
-
const cwd = resolveInputCwd(input);
|
|
478
|
-
const reportNote = "SwarmVault graph report exists at wiki/graph/report.md. Read it before broad grep/glob searching.";
|
|
479
|
-
|
|
480
|
-
if (!(await hasReport(cwd))) {
|
|
481
|
-
emit({});
|
|
482
|
-
process.exit(0);
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
if (mode === "session-start") {
|
|
486
|
-
await resetSession(cwd);
|
|
487
|
-
emit({});
|
|
488
|
-
process.exit(0);
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
const toolName = resolveToolName(input);
|
|
492
|
-
if (collectCandidatePaths(input).some((value) => isReportPath(value, cwd))) {
|
|
493
|
-
await markReportRead(cwd);
|
|
494
|
-
emit({});
|
|
495
|
-
process.exit(0);
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
if (isBroadSearchTool(toolName) && !(await hasSeenReport(cwd))) {
|
|
499
|
-
emit({
|
|
500
|
-
permissionDecision: "deny",
|
|
501
|
-
permissionDecisionReason: reportNote
|
|
502
|
-
});
|
|
503
|
-
process.exit(0);
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
emit({});
|
|
507
|
-
`;
|
|
508
|
-
}
|
|
509
|
-
function buildOpenCodePlugin() {
|
|
510
|
-
return `import path from "node:path";
|
|
511
|
-
|
|
512
|
-
const reportRelativePath = path.join("wiki", "graph", "report.md");
|
|
513
|
-
|
|
514
|
-
export const name = "swarmvault-graph-first";
|
|
515
|
-
|
|
516
|
-
export default async function swarmvaultGraphFirst({ client }) {
|
|
517
|
-
let reportSeen = false;
|
|
518
|
-
|
|
519
|
-
async function hasReport(cwd) {
|
|
520
|
-
try {
|
|
521
|
-
await Bun.file(path.join(cwd, reportRelativePath)).arrayBuffer();
|
|
522
|
-
return true;
|
|
523
|
-
} catch {
|
|
524
|
-
return false;
|
|
525
|
-
}
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
async function note(message) {
|
|
529
|
-
if (client?.app?.log) {
|
|
530
|
-
await client.app.log({
|
|
531
|
-
level: "info",
|
|
532
|
-
message
|
|
533
|
-
});
|
|
534
|
-
}
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
return {
|
|
538
|
-
async "session.created"(input) {
|
|
539
|
-
reportSeen = false;
|
|
540
|
-
const cwd = input?.session?.cwd ?? process.cwd();
|
|
541
|
-
if (await hasReport(cwd)) {
|
|
542
|
-
await note("SwarmVault graph report exists. Read wiki/graph/report.md before broad workspace searching.");
|
|
543
|
-
}
|
|
544
|
-
},
|
|
545
|
-
async "tool.execute.before"(input) {
|
|
546
|
-
const cwd = input?.session?.cwd ?? process.cwd();
|
|
547
|
-
if (!(await hasReport(cwd))) {
|
|
548
|
-
return;
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
const argsText = JSON.stringify(input?.args ?? {});
|
|
552
|
-
if (argsText.includes("wiki/graph/report.md")) {
|
|
553
|
-
reportSeen = true;
|
|
554
|
-
return;
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
if (!reportSeen && ["glob", "grep"].includes(String(input?.tool ?? ""))) {
|
|
558
|
-
await note("SwarmVault graph report exists. Read wiki/graph/report.md before broad workspace searching.");
|
|
559
|
-
}
|
|
560
|
-
}
|
|
561
|
-
};
|
|
562
|
-
}
|
|
563
|
-
`;
|
|
564
|
-
}
|
|
565
252
|
async function installGeminiHook(rootDir) {
|
|
566
253
|
const settingsPath = path.join(rootDir, ".gemini", "settings.json");
|
|
567
254
|
const scriptPath = path.join(rootDir, ".gemini", "hooks", "swarmvault-graph-first.js");
|
|
568
|
-
await writeOwnedFile(scriptPath,
|
|
255
|
+
await writeOwnedFile(scriptPath, await readBuiltHook("gemini.js"), true);
|
|
569
256
|
const { data: settings, warnings } = await readJsonWithWarnings(settingsPath, {}, ".gemini/settings.json");
|
|
570
257
|
if (warnings.length > 0 && await fileExists(settingsPath)) {
|
|
571
258
|
return { paths: [settingsPath, scriptPath], warnings };
|
|
@@ -632,7 +319,7 @@ async function installCopilotHook(rootDir) {
|
|
|
632
319
|
const hooksDir = path.join(rootDir, ".github", "hooks");
|
|
633
320
|
const scriptPath = path.join(hooksDir, "swarmvault-graph-first.js");
|
|
634
321
|
const configPath = path.join(hooksDir, "swarmvault-graph-first.json");
|
|
635
|
-
await writeOwnedFile(scriptPath,
|
|
322
|
+
await writeOwnedFile(scriptPath, await readBuiltHook("copilot.js"), true);
|
|
636
323
|
const config = {
|
|
637
324
|
version: copilotHookVersion,
|
|
638
325
|
hooks: {
|
|
@@ -663,7 +350,7 @@ async function installCopilotHook(rootDir) {
|
|
|
663
350
|
}
|
|
664
351
|
async function installOpenCodeHook(rootDir) {
|
|
665
352
|
const pluginPath = path.join(rootDir, ".opencode", "plugins", "swarmvault-graph-first.js");
|
|
666
|
-
await writeOwnedFile(pluginPath,
|
|
353
|
+
await writeOwnedFile(pluginPath, await readBuiltHook("opencode.js"));
|
|
667
354
|
return { paths: [pluginPath], warnings: [] };
|
|
668
355
|
}
|
|
669
356
|
function stableKeyForAgent(rootDir, agent) {
|
|
@@ -690,8 +377,7 @@ async function installAgent(rootDir, agent, options = {}) {
|
|
|
690
377
|
await upsertManagedBlock(target, buildManagedBlock("gemini"));
|
|
691
378
|
break;
|
|
692
379
|
case "cursor":
|
|
693
|
-
await writeOwnedFile(target,
|
|
694
|
-
`);
|
|
380
|
+
await writeOwnedFile(target, buildCursorRule());
|
|
695
381
|
break;
|
|
696
382
|
case "aider":
|
|
697
383
|
await upsertManagedBlock(target, buildManagedBlock("aider"));
|
|
@@ -1343,10 +1029,13 @@ function buildBenchmarkArtifact(input) {
|
|
|
1343
1029
|
const corpusTokens = Math.max(1, Math.round(input.corpusWords * (100 / 75)));
|
|
1344
1030
|
const perQuestion = input.perQuestion.filter((entry) => entry.queryTokens > 0).map((entry) => ({
|
|
1345
1031
|
...entry,
|
|
1346
|
-
reduction:
|
|
1032
|
+
// Honest reduction: negative values mean graph context is larger than the
|
|
1033
|
+
// full corpus, which is the truth on very small vaults. Clamping to zero
|
|
1034
|
+
// hid that signal.
|
|
1035
|
+
reduction: Number((1 - entry.queryTokens / Math.max(1, corpusTokens)).toFixed(3))
|
|
1347
1036
|
}));
|
|
1348
1037
|
const avgQueryTokens = perQuestion.length ? Math.max(1, Math.round(perQuestion.reduce((total, entry) => total + entry.queryTokens, 0) / perQuestion.length)) : 0;
|
|
1349
|
-
const reductionRatio = avgQueryTokens ? Number(
|
|
1038
|
+
const reductionRatio = avgQueryTokens ? Number((1 - avgQueryTokens / Math.max(1, corpusTokens)).toFixed(3)) : 0;
|
|
1350
1039
|
const uniqueVisitedNodes = new Set(perQuestion.flatMap((entry) => entry.visitedNodeIds)).size;
|
|
1351
1040
|
const summary = {
|
|
1352
1041
|
questionCount: input.questions.length,
|
|
@@ -1367,7 +1056,6 @@ function buildBenchmarkArtifact(input) {
|
|
|
1367
1056
|
reductionRatio,
|
|
1368
1057
|
sampleQuestions: input.questions,
|
|
1369
1058
|
perQuestion,
|
|
1370
|
-
questionResults: perQuestion,
|
|
1371
1059
|
summary
|
|
1372
1060
|
};
|
|
1373
1061
|
}
|
|
@@ -1728,6 +1416,7 @@ import { pathToFileURL } from "url";
|
|
|
1728
1416
|
import { Readability } from "@mozilla/readability";
|
|
1729
1417
|
import matter3 from "gray-matter";
|
|
1730
1418
|
import ignore from "ignore";
|
|
1419
|
+
import { isText } from "istextorbinary";
|
|
1731
1420
|
import { JSDOM as JSDOM2 } from "jsdom";
|
|
1732
1421
|
import mime from "mime-types";
|
|
1733
1422
|
import TurndownService2 from "turndown";
|
|
@@ -2172,104 +1861,431 @@ function treeSitterCompatibilityDiagnostic(language, error) {
|
|
|
2172
1861
|
column: 1
|
|
2173
1862
|
};
|
|
2174
1863
|
}
|
|
2175
|
-
function
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
return [];
|
|
1864
|
+
function flattenPythonDottedName(node) {
|
|
1865
|
+
if (!node) {
|
|
1866
|
+
return "";
|
|
2179
1867
|
}
|
|
2180
|
-
return
|
|
2181
|
-
const [specifier, alias] = item.split(/\s+as\s+/i);
|
|
2182
|
-
return {
|
|
2183
|
-
specifier: specifier.trim(),
|
|
2184
|
-
importedSymbols: [],
|
|
2185
|
-
namespaceImport: alias?.trim(),
|
|
2186
|
-
isExternal: !specifier.trim().startsWith("."),
|
|
2187
|
-
reExport: false
|
|
2188
|
-
};
|
|
2189
|
-
});
|
|
1868
|
+
return node.namedChildren.filter((child) => child?.type === "identifier").map((child) => child.text.trim()).filter(Boolean).join(".");
|
|
2190
1869
|
}
|
|
2191
|
-
function
|
|
2192
|
-
|
|
2193
|
-
|
|
1870
|
+
function flattenPythonRelativeImport(node) {
|
|
1871
|
+
if (!node) {
|
|
1872
|
+
return "";
|
|
1873
|
+
}
|
|
1874
|
+
const prefixNode = node.namedChildren.find((child) => child?.type === "import_prefix") ?? null;
|
|
1875
|
+
const prefix = prefixNode ? prefixNode.text.trim() : "";
|
|
1876
|
+
const moduleNode = node.namedChildren.find((child) => child?.type === "dotted_name") ?? null;
|
|
1877
|
+
const module = flattenPythonDottedName(moduleNode);
|
|
1878
|
+
return prefix + module;
|
|
1879
|
+
}
|
|
1880
|
+
function parsePythonImportStatement(node) {
|
|
1881
|
+
const imports = [];
|
|
1882
|
+
for (const child of node.namedChildren) {
|
|
1883
|
+
if (!child) {
|
|
1884
|
+
continue;
|
|
1885
|
+
}
|
|
1886
|
+
if (child.type === "dotted_name") {
|
|
1887
|
+
const specifier = flattenPythonDottedName(child);
|
|
1888
|
+
if (!specifier) {
|
|
1889
|
+
continue;
|
|
1890
|
+
}
|
|
1891
|
+
imports.push({
|
|
1892
|
+
specifier,
|
|
1893
|
+
importedSymbols: [],
|
|
1894
|
+
isExternal: !specifier.startsWith("."),
|
|
1895
|
+
reExport: false
|
|
1896
|
+
});
|
|
1897
|
+
} else if (child.type === "aliased_import") {
|
|
1898
|
+
const moduleNode = child.namedChildren.find((inner) => inner?.type === "dotted_name") ?? null;
|
|
1899
|
+
const aliasNode = child.namedChildren.find((inner) => inner?.type === "identifier") ?? null;
|
|
1900
|
+
const specifier = flattenPythonDottedName(moduleNode);
|
|
1901
|
+
if (!specifier) {
|
|
1902
|
+
continue;
|
|
1903
|
+
}
|
|
1904
|
+
imports.push({
|
|
1905
|
+
specifier,
|
|
1906
|
+
importedSymbols: [],
|
|
1907
|
+
namespaceImport: aliasNode?.text.trim(),
|
|
1908
|
+
isExternal: !specifier.startsWith("."),
|
|
1909
|
+
reExport: false
|
|
1910
|
+
});
|
|
1911
|
+
}
|
|
1912
|
+
}
|
|
1913
|
+
return imports;
|
|
1914
|
+
}
|
|
1915
|
+
function parsePythonFromImportStatement(node) {
|
|
1916
|
+
const children = node.namedChildren.filter((child) => child !== null);
|
|
1917
|
+
if (children.length === 0) {
|
|
1918
|
+
return [];
|
|
1919
|
+
}
|
|
1920
|
+
const [moduleNode, ...rest] = children;
|
|
1921
|
+
if (!moduleNode) {
|
|
2194
1922
|
return [];
|
|
2195
1923
|
}
|
|
1924
|
+
let specifier;
|
|
1925
|
+
if (moduleNode.type === "relative_import") {
|
|
1926
|
+
specifier = flattenPythonRelativeImport(moduleNode);
|
|
1927
|
+
} else if (moduleNode.type === "dotted_name") {
|
|
1928
|
+
specifier = flattenPythonDottedName(moduleNode);
|
|
1929
|
+
} else {
|
|
1930
|
+
return [];
|
|
1931
|
+
}
|
|
1932
|
+
if (!specifier) {
|
|
1933
|
+
return [];
|
|
1934
|
+
}
|
|
1935
|
+
const symbols = [];
|
|
1936
|
+
let hasWildcard = false;
|
|
1937
|
+
for (const entry of rest) {
|
|
1938
|
+
if (entry.type === "wildcard_import") {
|
|
1939
|
+
hasWildcard = true;
|
|
1940
|
+
continue;
|
|
1941
|
+
}
|
|
1942
|
+
if (entry.type === "dotted_name") {
|
|
1943
|
+
const name = flattenPythonDottedName(entry);
|
|
1944
|
+
if (name) {
|
|
1945
|
+
symbols.push(name);
|
|
1946
|
+
}
|
|
1947
|
+
continue;
|
|
1948
|
+
}
|
|
1949
|
+
if (entry.type === "aliased_import") {
|
|
1950
|
+
const moduleChild = entry.namedChildren.find((inner) => inner?.type === "dotted_name") ?? null;
|
|
1951
|
+
const aliasChild = entry.namedChildren.find((inner) => inner?.type === "identifier") ?? null;
|
|
1952
|
+
const baseName = flattenPythonDottedName(moduleChild);
|
|
1953
|
+
const aliasName = aliasChild?.text.trim();
|
|
1954
|
+
if (baseName) {
|
|
1955
|
+
symbols.push(aliasName ? `${baseName} as ${aliasName}` : baseName);
|
|
1956
|
+
}
|
|
1957
|
+
}
|
|
1958
|
+
}
|
|
1959
|
+
if (hasWildcard) {
|
|
1960
|
+
symbols.push("*");
|
|
1961
|
+
}
|
|
2196
1962
|
return [
|
|
2197
1963
|
{
|
|
2198
|
-
specifier
|
|
2199
|
-
importedSymbols:
|
|
2200
|
-
isExternal: !
|
|
1964
|
+
specifier,
|
|
1965
|
+
importedSymbols: symbols,
|
|
1966
|
+
isExternal: !specifier.startsWith("."),
|
|
2201
1967
|
reExport: false
|
|
2202
1968
|
}
|
|
2203
1969
|
];
|
|
2204
1970
|
}
|
|
2205
|
-
function parseGoImport(
|
|
2206
|
-
|
|
2207
|
-
|
|
1971
|
+
function parseGoImport(spec) {
|
|
1972
|
+
let alias;
|
|
1973
|
+
let dotImport = false;
|
|
1974
|
+
let blankImport = false;
|
|
1975
|
+
let specifier;
|
|
1976
|
+
for (const child of spec.namedChildren) {
|
|
1977
|
+
if (!child) {
|
|
1978
|
+
continue;
|
|
1979
|
+
}
|
|
1980
|
+
switch (child.type) {
|
|
1981
|
+
case "package_identifier":
|
|
1982
|
+
alias = child.text.trim();
|
|
1983
|
+
break;
|
|
1984
|
+
case "dot":
|
|
1985
|
+
dotImport = true;
|
|
1986
|
+
break;
|
|
1987
|
+
case "blank_identifier":
|
|
1988
|
+
blankImport = true;
|
|
1989
|
+
break;
|
|
1990
|
+
case "interpreted_string_literal":
|
|
1991
|
+
case "raw_string_literal": {
|
|
1992
|
+
const content = child.namedChildren.find(
|
|
1993
|
+
(inner) => inner?.type === "interpreted_string_literal_content" || inner?.type === "raw_string_literal_content"
|
|
1994
|
+
) ?? null;
|
|
1995
|
+
specifier = content ? content.text : child.text.replace(/^[`"]|[`"]$/g, "");
|
|
1996
|
+
break;
|
|
1997
|
+
}
|
|
1998
|
+
default:
|
|
1999
|
+
break;
|
|
2000
|
+
}
|
|
2001
|
+
}
|
|
2002
|
+
if (!specifier) {
|
|
2208
2003
|
return void 0;
|
|
2209
2004
|
}
|
|
2210
2005
|
return {
|
|
2211
|
-
specifier
|
|
2006
|
+
specifier,
|
|
2212
2007
|
importedSymbols: [],
|
|
2213
|
-
namespaceImport:
|
|
2214
|
-
isExternal: !
|
|
2008
|
+
namespaceImport: !dotImport && !blankImport ? alias : void 0,
|
|
2009
|
+
isExternal: !specifier.startsWith("."),
|
|
2215
2010
|
reExport: false
|
|
2216
2011
|
};
|
|
2217
2012
|
}
|
|
2218
|
-
function
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
)
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2013
|
+
function flattenRustPath(node) {
|
|
2014
|
+
if (!node) {
|
|
2015
|
+
return [];
|
|
2016
|
+
}
|
|
2017
|
+
if (node.type === "crate" || node.type === "self" || node.type === "super") {
|
|
2018
|
+
return [node.type];
|
|
2019
|
+
}
|
|
2020
|
+
if (node.type === "identifier") {
|
|
2021
|
+
return [node.text.trim()].filter(Boolean);
|
|
2022
|
+
}
|
|
2023
|
+
if (node.type === "scoped_identifier") {
|
|
2024
|
+
const segments = [];
|
|
2025
|
+
for (const child of node.namedChildren) {
|
|
2026
|
+
if (!child) {
|
|
2027
|
+
continue;
|
|
2028
|
+
}
|
|
2029
|
+
segments.push(...flattenRustPath(child));
|
|
2030
|
+
}
|
|
2031
|
+
return segments;
|
|
2032
|
+
}
|
|
2033
|
+
return node.namedChildren.filter((child) => child !== null).flatMap((child) => flattenRustPath(child));
|
|
2233
2034
|
}
|
|
2234
|
-
function
|
|
2235
|
-
|
|
2236
|
-
|
|
2035
|
+
function collectRustUseLeaves(node, prefix, leaves) {
|
|
2036
|
+
if (!node) {
|
|
2037
|
+
return;
|
|
2038
|
+
}
|
|
2039
|
+
switch (node.type) {
|
|
2040
|
+
case "scoped_identifier": {
|
|
2041
|
+
const segments = flattenRustPath(node);
|
|
2042
|
+
if (segments.length === 0) {
|
|
2043
|
+
return;
|
|
2044
|
+
}
|
|
2045
|
+
leaves.push({
|
|
2046
|
+
segments: [...prefix, ...segments],
|
|
2047
|
+
symbol: segments[segments.length - 1] ?? null,
|
|
2048
|
+
wildcard: false
|
|
2049
|
+
});
|
|
2050
|
+
return;
|
|
2051
|
+
}
|
|
2052
|
+
case "identifier":
|
|
2053
|
+
case "crate":
|
|
2054
|
+
case "self":
|
|
2055
|
+
case "super": {
|
|
2056
|
+
const combined = [...prefix];
|
|
2057
|
+
if (node.type === "self" && prefix.length > 0) {
|
|
2058
|
+
leaves.push({ segments: combined, symbol: null, wildcard: false });
|
|
2059
|
+
return;
|
|
2060
|
+
}
|
|
2061
|
+
combined.push(node.type === "identifier" ? node.text.trim() : node.type);
|
|
2062
|
+
leaves.push({
|
|
2063
|
+
segments: combined,
|
|
2064
|
+
symbol: combined[combined.length - 1] ?? null,
|
|
2065
|
+
wildcard: false
|
|
2066
|
+
});
|
|
2067
|
+
return;
|
|
2068
|
+
}
|
|
2069
|
+
case "scoped_use_list": {
|
|
2070
|
+
const pathNode = node.namedChildren[0] ?? null;
|
|
2071
|
+
const listNode = node.namedChildren[1] ?? null;
|
|
2072
|
+
const nextPrefix = [...prefix, ...flattenRustPath(pathNode)];
|
|
2073
|
+
collectRustUseLeaves(listNode, nextPrefix, leaves);
|
|
2074
|
+
return;
|
|
2075
|
+
}
|
|
2076
|
+
case "use_list": {
|
|
2077
|
+
for (const child of node.namedChildren) {
|
|
2078
|
+
collectRustUseLeaves(child, prefix, leaves);
|
|
2079
|
+
}
|
|
2080
|
+
return;
|
|
2081
|
+
}
|
|
2082
|
+
case "use_wildcard": {
|
|
2083
|
+
const pathNode = node.namedChildren[0] ?? null;
|
|
2084
|
+
const pathSegments = pathNode ? flattenRustPath(pathNode) : [];
|
|
2085
|
+
leaves.push({
|
|
2086
|
+
segments: [...prefix, ...pathSegments],
|
|
2087
|
+
symbol: null,
|
|
2088
|
+
wildcard: true
|
|
2089
|
+
});
|
|
2090
|
+
return;
|
|
2091
|
+
}
|
|
2092
|
+
case "use_as_clause": {
|
|
2093
|
+
const pathNode = node.childForFieldName("path") ?? node.namedChildren[0] ?? null;
|
|
2094
|
+
const aliasNode = node.childForFieldName("alias") ?? node.namedChildren[1] ?? null;
|
|
2095
|
+
const before = leaves.length;
|
|
2096
|
+
collectRustUseLeaves(pathNode, prefix, leaves);
|
|
2097
|
+
const alias = aliasNode?.text.trim();
|
|
2098
|
+
if (alias) {
|
|
2099
|
+
for (let index = before; index < leaves.length; index += 1) {
|
|
2100
|
+
const leaf = leaves[index];
|
|
2101
|
+
if (leaf) {
|
|
2102
|
+
leaf.alias = alias;
|
|
2103
|
+
}
|
|
2104
|
+
}
|
|
2105
|
+
}
|
|
2106
|
+
return;
|
|
2107
|
+
}
|
|
2108
|
+
default: {
|
|
2109
|
+
for (const child of node.namedChildren) {
|
|
2110
|
+
collectRustUseLeaves(child, prefix, leaves);
|
|
2111
|
+
}
|
|
2112
|
+
}
|
|
2113
|
+
}
|
|
2114
|
+
}
|
|
2115
|
+
function isRustPubUse(useNode) {
|
|
2116
|
+
for (const child of useNode.children) {
|
|
2117
|
+
if (!child) {
|
|
2118
|
+
continue;
|
|
2119
|
+
}
|
|
2120
|
+
if (child.type === "visibility_modifier") {
|
|
2121
|
+
return true;
|
|
2122
|
+
}
|
|
2123
|
+
if (child.type === "use") {
|
|
2124
|
+
return false;
|
|
2125
|
+
}
|
|
2126
|
+
}
|
|
2127
|
+
return false;
|
|
2128
|
+
}
|
|
2129
|
+
function parseRustUseDeclaration(useNode) {
|
|
2130
|
+
const inner = useNode.namedChildren.find((child) => child !== null) ?? null;
|
|
2131
|
+
if (!inner) {
|
|
2132
|
+
return [];
|
|
2133
|
+
}
|
|
2134
|
+
const leaves = [];
|
|
2135
|
+
collectRustUseLeaves(inner, [], leaves);
|
|
2136
|
+
if (leaves.length === 0) {
|
|
2137
|
+
return [];
|
|
2138
|
+
}
|
|
2139
|
+
const reExport = isRustPubUse(useNode);
|
|
2140
|
+
return leaves.map((leaf) => {
|
|
2141
|
+
const specifier = leaf.segments.join("::");
|
|
2142
|
+
const importedSymbols = leaf.wildcard ? ["*"] : leaf.alias && leaf.symbol ? [`${leaf.symbol} as ${leaf.alias}`] : leaf.symbol ? [leaf.symbol] : [];
|
|
2143
|
+
return {
|
|
2144
|
+
specifier,
|
|
2145
|
+
importedSymbols,
|
|
2146
|
+
isExternal: !/^(?:crate|self|super)(?:$|::)/.test(specifier),
|
|
2147
|
+
reExport
|
|
2148
|
+
};
|
|
2149
|
+
});
|
|
2150
|
+
}
|
|
2151
|
+
function flattenJavaScopedIdentifier(node) {
|
|
2152
|
+
if (!node) {
|
|
2153
|
+
return "";
|
|
2154
|
+
}
|
|
2155
|
+
if (node.type === "identifier") {
|
|
2156
|
+
return node.text.trim();
|
|
2157
|
+
}
|
|
2158
|
+
if (node.type === "scoped_identifier") {
|
|
2159
|
+
const head = node.namedChildren[0] ?? null;
|
|
2160
|
+
const tail = node.namedChildren[node.namedChildren.length - 1] ?? null;
|
|
2161
|
+
const headText = flattenJavaScopedIdentifier(head);
|
|
2162
|
+
const tailText = tail && tail !== head && tail.type === "identifier" ? tail.text.trim() : "";
|
|
2163
|
+
return headText && tailText ? `${headText}.${tailText}` : headText || tailText;
|
|
2164
|
+
}
|
|
2165
|
+
return node.text.trim();
|
|
2166
|
+
}
|
|
2167
|
+
function parseJavaImport(node) {
|
|
2168
|
+
const pathNode = node.namedChildren.find((child) => child?.type === "scoped_identifier") ?? null;
|
|
2169
|
+
const hasAsterisk = node.namedChildren.some((child) => child?.type === "asterisk");
|
|
2170
|
+
const pathText = flattenJavaScopedIdentifier(pathNode);
|
|
2171
|
+
const specifier = hasAsterisk ? `${pathText}.*` : pathText;
|
|
2172
|
+
const symbolName = hasAsterisk ? "" : (pathText.split(".").pop() ?? "").trim();
|
|
2237
2173
|
return {
|
|
2238
|
-
specifier
|
|
2174
|
+
specifier,
|
|
2239
2175
|
importedSymbols: symbolName ? [symbolName] : [],
|
|
2240
2176
|
isExternal: true,
|
|
2241
2177
|
reExport: false
|
|
2242
2178
|
};
|
|
2243
2179
|
}
|
|
2244
|
-
function
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2180
|
+
function flattenKotlinIdentifier(node) {
|
|
2181
|
+
if (!node) {
|
|
2182
|
+
return "";
|
|
2183
|
+
}
|
|
2184
|
+
if (node.type === "simple_identifier") {
|
|
2185
|
+
return node.text.trim();
|
|
2248
2186
|
}
|
|
2249
|
-
|
|
2250
|
-
|
|
2187
|
+
return node.namedChildren.filter((child) => child?.type === "simple_identifier").map((child) => child.text.trim()).filter(Boolean).join(".");
|
|
2188
|
+
}
|
|
2189
|
+
function parseKotlinImport(header) {
|
|
2190
|
+
const identifierNode = header.namedChildren.find((child) => child?.type === "identifier") ?? null;
|
|
2191
|
+
const specifier = flattenKotlinIdentifier(identifierNode);
|
|
2251
2192
|
if (!specifier) {
|
|
2252
2193
|
return void 0;
|
|
2253
2194
|
}
|
|
2195
|
+
const hasWildcard = header.descendantsOfType("wildcard_import").some((child) => child !== null);
|
|
2196
|
+
const aliasNode = header.namedChildren.find((child) => child?.type === "import_alias") ?? null;
|
|
2197
|
+
const aliasName = aliasNode ? flattenKotlinIdentifier(aliasNode.namedChildren.find((child) => child?.type === "type_identifier") ?? null) || aliasNode.text.replace(/^as\s+/, "").trim() : void 0;
|
|
2254
2198
|
return {
|
|
2255
|
-
specifier,
|
|
2256
|
-
importedSymbols: [],
|
|
2257
|
-
namespaceImport:
|
|
2258
|
-
isExternal:
|
|
2199
|
+
specifier: hasWildcard ? `${specifier}.*` : specifier,
|
|
2200
|
+
importedSymbols: hasWildcard ? ["*"] : [],
|
|
2201
|
+
namespaceImport: aliasName || void 0,
|
|
2202
|
+
isExternal: true,
|
|
2259
2203
|
reExport: false
|
|
2260
2204
|
};
|
|
2261
2205
|
}
|
|
2262
|
-
function
|
|
2263
|
-
|
|
2264
|
-
|
|
2206
|
+
function flattenScalaStableIdentifier(node) {
|
|
2207
|
+
if (!node) {
|
|
2208
|
+
return "";
|
|
2209
|
+
}
|
|
2210
|
+
if (node.type === "identifier") {
|
|
2211
|
+
return node.text.trim();
|
|
2212
|
+
}
|
|
2213
|
+
if (node.type === "stable_identifier") {
|
|
2214
|
+
return node.namedChildren.filter((child) => child !== null).map((child) => flattenScalaStableIdentifier(child)).filter(Boolean).join(".");
|
|
2215
|
+
}
|
|
2216
|
+
return node.text.trim();
|
|
2217
|
+
}
|
|
2218
|
+
function parseScalaImport(node) {
|
|
2219
|
+
const pathNode = node.namedChildren.find((child) => child?.type === "stable_identifier") ?? node.namedChildren.find((child) => child?.type === "identifier") ?? null;
|
|
2220
|
+
const basePath = flattenScalaStableIdentifier(pathNode);
|
|
2221
|
+
if (!basePath) {
|
|
2265
2222
|
return [];
|
|
2266
2223
|
}
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2224
|
+
const selectorsNode = node.namedChildren.find((child) => child?.type === "import_selectors") ?? null;
|
|
2225
|
+
const wildcardNode = node.namedChildren.find((child) => child?.type === "wildcard") ?? null;
|
|
2226
|
+
if (selectorsNode) {
|
|
2227
|
+
const results = [];
|
|
2228
|
+
for (const selector of selectorsNode.namedChildren) {
|
|
2229
|
+
if (!selector) {
|
|
2230
|
+
continue;
|
|
2231
|
+
}
|
|
2232
|
+
if (selector.type === "identifier") {
|
|
2233
|
+
const symbol2 = selector.text.trim();
|
|
2234
|
+
if (symbol2) {
|
|
2235
|
+
results.push({
|
|
2236
|
+
specifier: basePath,
|
|
2237
|
+
importedSymbols: [symbol2],
|
|
2238
|
+
isExternal: !basePath.startsWith("."),
|
|
2239
|
+
reExport: false
|
|
2240
|
+
});
|
|
2241
|
+
}
|
|
2242
|
+
continue;
|
|
2243
|
+
}
|
|
2244
|
+
if (selector.type === "renamed_identifier") {
|
|
2245
|
+
const idChildren = selector.namedChildren.filter((child) => child?.type === "identifier");
|
|
2246
|
+
const [original, alias] = [idChildren[0]?.text.trim(), idChildren[1]?.text.trim()];
|
|
2247
|
+
if (original) {
|
|
2248
|
+
results.push({
|
|
2249
|
+
specifier: basePath,
|
|
2250
|
+
importedSymbols: [alias ? `${original} as ${alias}` : original],
|
|
2251
|
+
isExternal: !basePath.startsWith("."),
|
|
2252
|
+
reExport: false
|
|
2253
|
+
});
|
|
2254
|
+
}
|
|
2255
|
+
continue;
|
|
2256
|
+
}
|
|
2257
|
+
if (selector.type === "wildcard") {
|
|
2258
|
+
results.push({
|
|
2259
|
+
specifier: basePath,
|
|
2260
|
+
importedSymbols: ["*"],
|
|
2261
|
+
isExternal: !basePath.startsWith("."),
|
|
2262
|
+
reExport: false
|
|
2263
|
+
});
|
|
2264
|
+
}
|
|
2265
|
+
}
|
|
2266
|
+
return results;
|
|
2267
|
+
}
|
|
2268
|
+
if (wildcardNode) {
|
|
2269
|
+
return [
|
|
2270
|
+
{
|
|
2271
|
+
specifier: basePath,
|
|
2272
|
+
importedSymbols: ["*"],
|
|
2273
|
+
isExternal: !basePath.startsWith("."),
|
|
2274
|
+
reExport: false
|
|
2275
|
+
}
|
|
2276
|
+
];
|
|
2277
|
+
}
|
|
2278
|
+
const segments = basePath.split(".");
|
|
2279
|
+
const symbol = segments.pop() ?? basePath;
|
|
2280
|
+
const parent = segments.join(".");
|
|
2281
|
+
return [
|
|
2282
|
+
{
|
|
2283
|
+
specifier: parent || basePath,
|
|
2284
|
+
importedSymbols: [symbol],
|
|
2285
|
+
isExternal: !basePath.startsWith("."),
|
|
2286
|
+
reExport: false
|
|
2287
|
+
}
|
|
2288
|
+
];
|
|
2273
2289
|
}
|
|
2274
2290
|
function bashCommandName(commandNode) {
|
|
2275
2291
|
if (!commandNode) {
|
|
@@ -2391,54 +2407,164 @@ function parseZigImport(node) {
|
|
|
2391
2407
|
reExport: false
|
|
2392
2408
|
};
|
|
2393
2409
|
}
|
|
2394
|
-
function
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
return {
|
|
2398
|
-
specifier: aliasMatch[2].trim(),
|
|
2399
|
-
importedSymbols: [],
|
|
2400
|
-
namespaceImport: aliasMatch[1],
|
|
2401
|
-
isExternal: !aliasMatch[2].trim().startsWith("."),
|
|
2402
|
-
reExport: false
|
|
2403
|
-
};
|
|
2410
|
+
function flattenCSharpQualifiedName(node) {
|
|
2411
|
+
if (!node) {
|
|
2412
|
+
return "";
|
|
2404
2413
|
}
|
|
2405
|
-
|
|
2406
|
-
|
|
2414
|
+
if (node.type === "identifier") {
|
|
2415
|
+
return node.text.trim();
|
|
2416
|
+
}
|
|
2417
|
+
if (node.type === "qualified_name") {
|
|
2418
|
+
const [head, tail] = [node.namedChildren[0] ?? null, node.namedChildren[1] ?? null];
|
|
2419
|
+
const headText = flattenCSharpQualifiedName(head);
|
|
2420
|
+
const tailText = tail?.type === "identifier" ? tail.text.trim() : flattenCSharpQualifiedName(tail);
|
|
2421
|
+
return headText && tailText ? `${headText}.${tailText}` : headText || tailText;
|
|
2422
|
+
}
|
|
2423
|
+
return node.text.trim();
|
|
2424
|
+
}
|
|
2425
|
+
function parseCSharpUsing(node) {
|
|
2426
|
+
const namedChildren = node.namedChildren.filter((child) => child !== null);
|
|
2427
|
+
if (namedChildren.length === 0) {
|
|
2428
|
+
return void 0;
|
|
2429
|
+
}
|
|
2430
|
+
let aliasName;
|
|
2431
|
+
let pathNode = null;
|
|
2432
|
+
if (namedChildren.length >= 2 && namedChildren[0]?.type === "identifier" && namedChildren[1]) {
|
|
2433
|
+
aliasName = namedChildren[0].text.trim();
|
|
2434
|
+
pathNode = namedChildren[1];
|
|
2435
|
+
} else {
|
|
2436
|
+
pathNode = namedChildren[0] ?? null;
|
|
2437
|
+
}
|
|
2438
|
+
const specifier = flattenCSharpQualifiedName(pathNode);
|
|
2439
|
+
if (!specifier) {
|
|
2407
2440
|
return void 0;
|
|
2408
2441
|
}
|
|
2409
2442
|
return {
|
|
2410
|
-
specifier
|
|
2443
|
+
specifier,
|
|
2411
2444
|
importedSymbols: [],
|
|
2412
|
-
|
|
2445
|
+
namespaceImport: aliasName,
|
|
2446
|
+
isExternal: !specifier.startsWith("."),
|
|
2413
2447
|
reExport: false
|
|
2414
2448
|
};
|
|
2415
2449
|
}
|
|
2416
|
-
function
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
return
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2450
|
+
function flattenPhpQualifiedName(node) {
|
|
2451
|
+
if (!node) {
|
|
2452
|
+
return "";
|
|
2453
|
+
}
|
|
2454
|
+
if (node.type === "name") {
|
|
2455
|
+
return node.text.trim();
|
|
2456
|
+
}
|
|
2457
|
+
if (node.type === "namespace_name") {
|
|
2458
|
+
return node.namedChildren.filter((child) => child?.type === "name").map((child) => child.text.trim()).filter(Boolean).join("\\");
|
|
2459
|
+
}
|
|
2460
|
+
if (node.type === "qualified_name") {
|
|
2461
|
+
const parts = [];
|
|
2462
|
+
for (const child of node.namedChildren) {
|
|
2463
|
+
if (!child) {
|
|
2464
|
+
continue;
|
|
2465
|
+
}
|
|
2466
|
+
if (child.type === "namespace_name") {
|
|
2467
|
+
parts.push(flattenPhpQualifiedName(child));
|
|
2468
|
+
} else if (child.type === "name") {
|
|
2469
|
+
parts.push(child.text.trim());
|
|
2470
|
+
}
|
|
2471
|
+
}
|
|
2472
|
+
return parts.filter(Boolean).join("\\");
|
|
2473
|
+
}
|
|
2474
|
+
return node.text.trim();
|
|
2429
2475
|
}
|
|
2430
|
-
function
|
|
2431
|
-
const
|
|
2432
|
-
|
|
2476
|
+
function parsePhpUseClause(clause, prefix) {
|
|
2477
|
+
const names = clause.namedChildren.filter((child) => child?.type === "name");
|
|
2478
|
+
const qualified = clause.namedChildren.find((child) => child?.type === "qualified_name") ?? null;
|
|
2479
|
+
let specifier;
|
|
2480
|
+
let aliasName;
|
|
2481
|
+
if (qualified) {
|
|
2482
|
+
specifier = flattenPhpQualifiedName(qualified);
|
|
2483
|
+
if (names.length >= 1 && names[0]) {
|
|
2484
|
+
aliasName = names[0].text.trim();
|
|
2485
|
+
}
|
|
2486
|
+
} else if (names.length >= 1 && names[0]) {
|
|
2487
|
+
specifier = names[0].text.trim();
|
|
2488
|
+
if (names.length >= 2 && names[1]) {
|
|
2489
|
+
aliasName = names[1].text.trim();
|
|
2490
|
+
}
|
|
2491
|
+
} else {
|
|
2433
2492
|
return void 0;
|
|
2434
2493
|
}
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2494
|
+
if (prefix && specifier) {
|
|
2495
|
+
specifier = `${prefix}\\${specifier}`;
|
|
2496
|
+
}
|
|
2497
|
+
if (!specifier) {
|
|
2498
|
+
return void 0;
|
|
2499
|
+
}
|
|
2500
|
+
return {
|
|
2501
|
+
specifier,
|
|
2502
|
+
importedSymbols: [],
|
|
2503
|
+
namespaceImport: aliasName,
|
|
2504
|
+
isExternal: true,
|
|
2505
|
+
reExport: false
|
|
2506
|
+
};
|
|
2507
|
+
}
|
|
2508
|
+
function parsePhpUse(node) {
|
|
2509
|
+
const results = [];
|
|
2510
|
+
const groupNode = node.namedChildren.find((child) => child?.type === "namespace_use_group") ?? null;
|
|
2511
|
+
if (groupNode) {
|
|
2512
|
+
const prefixNode = node.namedChildren.find((child) => child?.type === "namespace_name") ?? null;
|
|
2513
|
+
const prefix = flattenPhpQualifiedName(prefixNode);
|
|
2514
|
+
for (const clause of groupNode.namedChildren) {
|
|
2515
|
+
if (!clause || clause.type !== "namespace_use_clause") {
|
|
2516
|
+
continue;
|
|
2517
|
+
}
|
|
2518
|
+
const parsed = parsePhpUseClause(clause, prefix);
|
|
2519
|
+
if (parsed) {
|
|
2520
|
+
results.push(parsed);
|
|
2521
|
+
}
|
|
2522
|
+
}
|
|
2523
|
+
return results;
|
|
2524
|
+
}
|
|
2525
|
+
for (const child of node.namedChildren) {
|
|
2526
|
+
if (!child || child.type !== "namespace_use_clause") {
|
|
2527
|
+
continue;
|
|
2528
|
+
}
|
|
2529
|
+
const parsed = parsePhpUseClause(child, "");
|
|
2530
|
+
if (parsed) {
|
|
2531
|
+
results.push(parsed);
|
|
2532
|
+
}
|
|
2533
|
+
}
|
|
2534
|
+
return results;
|
|
2535
|
+
}
|
|
2536
|
+
function parseCppInclude(node) {
|
|
2537
|
+
for (const child of node.namedChildren) {
|
|
2538
|
+
if (!child) {
|
|
2539
|
+
continue;
|
|
2540
|
+
}
|
|
2541
|
+
if (child.type === "system_lib_string") {
|
|
2542
|
+
const specifier = child.text.replace(/^</, "").replace(/>$/, "").trim();
|
|
2543
|
+
if (!specifier) {
|
|
2544
|
+
return void 0;
|
|
2545
|
+
}
|
|
2546
|
+
return {
|
|
2547
|
+
specifier,
|
|
2548
|
+
importedSymbols: [],
|
|
2549
|
+
isExternal: true,
|
|
2550
|
+
reExport: false
|
|
2551
|
+
};
|
|
2552
|
+
}
|
|
2553
|
+
if (child.type === "string_literal") {
|
|
2554
|
+
const contentNode = child.namedChildren.find((inner) => inner?.type === "string_content") ?? null;
|
|
2555
|
+
const specifier = (contentNode?.text ?? child.text.replace(/^"|"$/g, "")).trim();
|
|
2556
|
+
if (!specifier) {
|
|
2557
|
+
return void 0;
|
|
2558
|
+
}
|
|
2559
|
+
return {
|
|
2560
|
+
specifier,
|
|
2561
|
+
importedSymbols: [],
|
|
2562
|
+
isExternal: false,
|
|
2563
|
+
reExport: false
|
|
2564
|
+
};
|
|
2565
|
+
}
|
|
2566
|
+
}
|
|
2567
|
+
return void 0;
|
|
2442
2568
|
}
|
|
2443
2569
|
function rubyStringContent(node) {
|
|
2444
2570
|
if (!node) {
|
|
@@ -2447,14 +2573,19 @@ function rubyStringContent(node) {
|
|
|
2447
2573
|
const contentNode = node.descendantsOfType(["string_content", "simple_symbol", "bare_string"]).find((item) => item !== null) ?? null;
|
|
2448
2574
|
return contentNode?.text.trim() || void 0;
|
|
2449
2575
|
}
|
|
2576
|
+
function normalizePowerShellDotSourceSpecifier(raw) {
|
|
2577
|
+
const unquoted = raw.replace(/^['"]+|['"]+$/g, "").trim();
|
|
2578
|
+
const withoutScriptRoot = unquoted.replace(/^\$PSScriptRoot(?:[\\/]+|$)/i, "./");
|
|
2579
|
+
return withoutScriptRoot.replace(/\\/g, "/");
|
|
2580
|
+
}
|
|
2450
2581
|
function parsePowerShellImport(commandNode) {
|
|
2451
2582
|
const commandName = commandNode.descendantsOfType(["command_name", "command_name_expr"]).find((item) => item !== null)?.text.trim();
|
|
2452
2583
|
const genericTokens = commandNode.descendantsOfType("generic_token").filter((item) => item !== null).map((item) => item.text.trim());
|
|
2453
2584
|
if (commandNode.namedChildren.some((child) => child?.type === "command_invokation_operator")) {
|
|
2454
|
-
const
|
|
2455
|
-
if (
|
|
2585
|
+
const raw = commandName?.trim();
|
|
2586
|
+
if (raw) {
|
|
2456
2587
|
return {
|
|
2457
|
-
specifier,
|
|
2588
|
+
specifier: normalizePowerShellDotSourceSpecifier(raw),
|
|
2458
2589
|
importedSymbols: [],
|
|
2459
2590
|
isExternal: false,
|
|
2460
2591
|
reExport: false
|
|
@@ -2799,11 +2930,11 @@ function pythonCodeAnalysis(manifest, rootNode, diagnostics) {
|
|
|
2799
2930
|
continue;
|
|
2800
2931
|
}
|
|
2801
2932
|
if (child.type === "import_statement") {
|
|
2802
|
-
imports.push(...parsePythonImportStatement(child
|
|
2933
|
+
imports.push(...parsePythonImportStatement(child));
|
|
2803
2934
|
continue;
|
|
2804
2935
|
}
|
|
2805
2936
|
if (child.type === "import_from_statement") {
|
|
2806
|
-
imports.push(...parsePythonFromImportStatement(child
|
|
2937
|
+
imports.push(...parsePythonFromImportStatement(child));
|
|
2807
2938
|
continue;
|
|
2808
2939
|
}
|
|
2809
2940
|
if (child.type === "class_definition") {
|
|
@@ -2858,7 +2989,7 @@ function goCodeAnalysis(manifest, rootNode, diagnostics) {
|
|
|
2858
2989
|
}
|
|
2859
2990
|
if (child.type === "import_declaration") {
|
|
2860
2991
|
for (const spec of child.descendantsOfType("import_spec")) {
|
|
2861
|
-
const parsed = spec ? parseGoImport(spec
|
|
2992
|
+
const parsed = spec ? parseGoImport(spec) : void 0;
|
|
2862
2993
|
if (parsed) {
|
|
2863
2994
|
imports.push(parsed);
|
|
2864
2995
|
}
|
|
@@ -2932,7 +3063,22 @@ function rustCodeAnalysis(manifest, rootNode, diagnostics) {
|
|
|
2932
3063
|
continue;
|
|
2933
3064
|
}
|
|
2934
3065
|
if (child.type === "use_declaration") {
|
|
2935
|
-
imports.push(
|
|
3066
|
+
imports.push(...parseRustUseDeclaration(child));
|
|
3067
|
+
continue;
|
|
3068
|
+
}
|
|
3069
|
+
if (child.type === "mod_item") {
|
|
3070
|
+
const hasInlineBody = child.namedChildren.some((item) => item?.type === "declaration_list");
|
|
3071
|
+
if (!hasInlineBody) {
|
|
3072
|
+
const modName = extractIdentifier(child.childForFieldName("name") ?? findNamedChild(child, "identifier"));
|
|
3073
|
+
if (modName) {
|
|
3074
|
+
imports.push({
|
|
3075
|
+
specifier: `self::${modName}`,
|
|
3076
|
+
importedSymbols: [],
|
|
3077
|
+
isExternal: false,
|
|
3078
|
+
reExport: false
|
|
3079
|
+
});
|
|
3080
|
+
}
|
|
3081
|
+
}
|
|
2936
3082
|
continue;
|
|
2937
3083
|
}
|
|
2938
3084
|
const name = child.type === "function_item" ? extractIdentifier(child.childForFieldName("name")) : extractIdentifier(child.childForFieldName("name"));
|
|
@@ -2996,11 +3142,15 @@ function javaCodeAnalysis(manifest, rootNode, diagnostics) {
|
|
|
2996
3142
|
continue;
|
|
2997
3143
|
}
|
|
2998
3144
|
if (child.type === "package_declaration") {
|
|
2999
|
-
|
|
3145
|
+
const pathNode = child.namedChildren.find((inner) => inner?.type === "scoped_identifier" || inner?.type === "identifier") ?? null;
|
|
3146
|
+
const flattened = flattenJavaScopedIdentifier(pathNode);
|
|
3147
|
+
if (flattened) {
|
|
3148
|
+
packageName = flattened;
|
|
3149
|
+
}
|
|
3000
3150
|
continue;
|
|
3001
3151
|
}
|
|
3002
3152
|
if (child.type === "import_declaration") {
|
|
3003
|
-
imports.push(parseJavaImport(child
|
|
3153
|
+
imports.push(parseJavaImport(child));
|
|
3004
3154
|
continue;
|
|
3005
3155
|
}
|
|
3006
3156
|
const name = extractIdentifier(child.childForFieldName("name"));
|
|
@@ -3088,7 +3238,7 @@ function kotlinCodeAnalysis(manifest, rootNode, diagnostics) {
|
|
|
3088
3238
|
}
|
|
3089
3239
|
if (child.type === "import_list") {
|
|
3090
3240
|
for (const importNode of child.descendantsOfType("import_header").filter((item) => item !== null)) {
|
|
3091
|
-
const parsed = parseKotlinImport(importNode
|
|
3241
|
+
const parsed = parseKotlinImport(importNode);
|
|
3092
3242
|
if (parsed) {
|
|
3093
3243
|
imports.push(parsed);
|
|
3094
3244
|
}
|
|
@@ -3178,7 +3328,7 @@ function scalaCodeAnalysis(manifest, rootNode, diagnostics) {
|
|
|
3178
3328
|
continue;
|
|
3179
3329
|
}
|
|
3180
3330
|
if (child.type === "import_declaration") {
|
|
3181
|
-
imports.push(...parseScalaImport(child
|
|
3331
|
+
imports.push(...parseScalaImport(child));
|
|
3182
3332
|
continue;
|
|
3183
3333
|
}
|
|
3184
3334
|
if (child.type === "function_definition") {
|
|
@@ -3359,7 +3509,7 @@ function csharpCodeAnalysis(manifest, rootNode, diagnostics) {
|
|
|
3359
3509
|
continue;
|
|
3360
3510
|
}
|
|
3361
3511
|
if (child.type === "using_directive") {
|
|
3362
|
-
const parsed = parseCSharpUsing(child
|
|
3512
|
+
const parsed = parseCSharpUsing(child);
|
|
3363
3513
|
if (parsed) {
|
|
3364
3514
|
imports.push(parsed);
|
|
3365
3515
|
}
|
|
@@ -3451,7 +3601,7 @@ function phpCodeAnalysis(manifest, rootNode, diagnostics) {
|
|
|
3451
3601
|
continue;
|
|
3452
3602
|
}
|
|
3453
3603
|
if (child.type === "namespace_use_declaration") {
|
|
3454
|
-
imports.push(...parsePhpUse(child
|
|
3604
|
+
imports.push(...parsePhpUse(child));
|
|
3455
3605
|
continue;
|
|
3456
3606
|
}
|
|
3457
3607
|
const name = extractIdentifier(child.childForFieldName("name"));
|
|
@@ -3518,8 +3668,9 @@ function rubyCodeAnalysis(manifest, rootNode, diagnostics) {
|
|
|
3518
3668
|
if (callee === "require" || callee === "require_relative") {
|
|
3519
3669
|
const specifier = rubyStringContent(child.childForFieldName("arguments") ?? child.namedChildren.at(1) ?? null);
|
|
3520
3670
|
if (specifier) {
|
|
3671
|
+
const normalizedSpecifier = callee === "require_relative" && !specifier.startsWith(".") && !specifier.startsWith("/") ? `./${specifier}` : specifier;
|
|
3521
3672
|
imports.push({
|
|
3522
|
-
specifier,
|
|
3673
|
+
specifier: normalizedSpecifier,
|
|
3523
3674
|
importedSymbols: [],
|
|
3524
3675
|
isExternal: callee === "require" && !specifier.startsWith("."),
|
|
3525
3676
|
reExport: false
|
|
@@ -3674,7 +3825,7 @@ function cFamilyCodeAnalysis(manifest, language, rootNode, diagnostics) {
|
|
|
3674
3825
|
continue;
|
|
3675
3826
|
}
|
|
3676
3827
|
if (child.type === "preproc_include") {
|
|
3677
|
-
const parsed = parseCppInclude(child
|
|
3828
|
+
const parsed = parseCppInclude(child);
|
|
3678
3829
|
if (parsed) {
|
|
3679
3830
|
imports.push(parsed);
|
|
3680
3831
|
}
|
|
@@ -3766,7 +3917,7 @@ async function analyzeTreeSitterCode(manifest, content, language) {
|
|
|
3766
3917
|
};
|
|
3767
3918
|
}
|
|
3768
3919
|
try {
|
|
3769
|
-
const diagnostics = diagnosticsFromTree(tree.rootNode);
|
|
3920
|
+
const diagnostics = language === "lua" ? [] : diagnosticsFromTree(tree.rootNode);
|
|
3770
3921
|
const rationales = extractTreeSitterRationales(manifest, language, tree.rootNode);
|
|
3771
3922
|
switch (language) {
|
|
3772
3923
|
case "bash":
|
|
@@ -4509,7 +4660,13 @@ function modulePageTitle(manifest) {
|
|
|
4509
4660
|
}
|
|
4510
4661
|
function importResolutionCandidates(basePath, specifier, extensions) {
|
|
4511
4662
|
const resolved = path6.posix.normalize(path6.posix.join(path6.posix.dirname(basePath), specifier));
|
|
4512
|
-
|
|
4663
|
+
const resolvedExt = path6.posix.extname(resolved);
|
|
4664
|
+
if (resolvedExt) {
|
|
4665
|
+
if (extensions.includes(resolvedExt)) {
|
|
4666
|
+
const resolvedBase = resolved.slice(0, -resolvedExt.length);
|
|
4667
|
+
const candidates = [resolved, ...extensions.map((extension) => `${resolvedBase}${extension}`)];
|
|
4668
|
+
return uniqueBy(candidates, (candidate) => candidate);
|
|
4669
|
+
}
|
|
4513
4670
|
return [resolved];
|
|
4514
4671
|
}
|
|
4515
4672
|
const direct = extensions.map((extension) => path6.posix.normalize(`${resolved}${extension}`));
|
|
@@ -4602,13 +4759,36 @@ async function readNearestGoModulePath(startPath, cache) {
|
|
|
4602
4759
|
current = parent;
|
|
4603
4760
|
}
|
|
4604
4761
|
}
|
|
4605
|
-
function
|
|
4606
|
-
const withoutExt = stripCodeExtension2(normalizeAlias(repoRelativePath));
|
|
4607
|
-
|
|
4608
|
-
|
|
4609
|
-
|
|
4762
|
+
function rustModuleAliases(repoRelativePath) {
|
|
4763
|
+
const withoutExt = stripCodeExtension2(normalizeAlias(repoRelativePath)).replace(/\/mod$/i, "");
|
|
4764
|
+
if (!withoutExt) {
|
|
4765
|
+
return [];
|
|
4766
|
+
}
|
|
4767
|
+
const result = [];
|
|
4768
|
+
const push = (moduleTail) => {
|
|
4769
|
+
const trimmed = moduleTail.replace(/^\/+|\/+$/g, "");
|
|
4770
|
+
if (!trimmed || trimmed === "lib" || trimmed === "main") {
|
|
4771
|
+
result.push("crate");
|
|
4772
|
+
return;
|
|
4773
|
+
}
|
|
4774
|
+
const rootStripped = trimmed.replace(/\/(?:lib|main)$/i, "");
|
|
4775
|
+
if (rootStripped !== trimmed && rootStripped) {
|
|
4776
|
+
result.push(`crate::${rootStripped.replace(/\//g, "::")}`);
|
|
4777
|
+
}
|
|
4778
|
+
result.push(`crate::${trimmed.replace(/\//g, "::")}`);
|
|
4779
|
+
};
|
|
4780
|
+
const srcIdx = withoutExt.lastIndexOf("/src/");
|
|
4781
|
+
if (srcIdx >= 0) {
|
|
4782
|
+
push(withoutExt.slice(srcIdx + "/src/".length));
|
|
4783
|
+
}
|
|
4784
|
+
if (withoutExt.startsWith("src/")) {
|
|
4785
|
+
push(withoutExt.slice("src/".length));
|
|
4610
4786
|
}
|
|
4611
|
-
|
|
4787
|
+
const segments = withoutExt.split("/").filter(Boolean);
|
|
4788
|
+
for (let start = 0; start < segments.length; start += 1) {
|
|
4789
|
+
push(segments.slice(start).join("/"));
|
|
4790
|
+
}
|
|
4791
|
+
return uniqueBy(result.filter(Boolean), (item) => item);
|
|
4612
4792
|
}
|
|
4613
4793
|
function candidateExtensionsFor(language) {
|
|
4614
4794
|
switch (language) {
|
|
@@ -4684,7 +4864,9 @@ async function buildCodeIndex(rootDir, manifests, analyses) {
|
|
|
4684
4864
|
break;
|
|
4685
4865
|
case "rust":
|
|
4686
4866
|
if (repoRelativePath) {
|
|
4687
|
-
|
|
4867
|
+
for (const alias of rustModuleAliases(repoRelativePath)) {
|
|
4868
|
+
recordAlias(aliases, alias);
|
|
4869
|
+
}
|
|
4688
4870
|
}
|
|
4689
4871
|
break;
|
|
4690
4872
|
case "go": {
|
|
@@ -4795,6 +4977,9 @@ function aliasMatches(lookup, ...aliases) {
|
|
|
4795
4977
|
(entry) => entry.sourceId
|
|
4796
4978
|
);
|
|
4797
4979
|
}
|
|
4980
|
+
function aliasMatchesExact(lookup, alias) {
|
|
4981
|
+
return lookup.byAlias.get(normalizeAlias(alias)) ?? [];
|
|
4982
|
+
}
|
|
4798
4983
|
function repoPathMatches(lookup, ...repoPaths) {
|
|
4799
4984
|
return uniqueBy(
|
|
4800
4985
|
repoPaths.map((repoPath) => lookup.byRepoPath.get(normalizeAlias(repoPath))).filter((entry) => Boolean(entry)),
|
|
@@ -4812,30 +4997,119 @@ function resolvePythonRelativeAliases(repoRelativePath, specifier) {
|
|
|
4812
4997
|
}
|
|
4813
4998
|
function resolveRustAliases(manifest, specifier) {
|
|
4814
4999
|
const repoRelativePath = manifest.repoRelativePath ? normalizeAlias(manifest.repoRelativePath) : "";
|
|
4815
|
-
|
|
4816
|
-
if (!specifier.startsWith("self::") && !specifier.startsWith("super::")) {
|
|
5000
|
+
if (!specifier.startsWith("self::") && !specifier.startsWith("super::") && specifier !== "self" && specifier !== "super") {
|
|
4817
5001
|
return [specifier];
|
|
4818
5002
|
}
|
|
4819
|
-
|
|
5003
|
+
const candidateAliases = repoRelativePath ? rustModuleAliases(repoRelativePath) : [];
|
|
5004
|
+
if (candidateAliases.length === 0) {
|
|
4820
5005
|
return [];
|
|
4821
5006
|
}
|
|
4822
|
-
const
|
|
4823
|
-
|
|
4824
|
-
|
|
5007
|
+
const tailAfter = specifier.startsWith("self::") ? specifier.slice("self::".length) : specifier.startsWith("super::") ? specifier.slice("super::".length) : "";
|
|
5008
|
+
const superRelative = specifier.startsWith("super");
|
|
5009
|
+
const expansions = [];
|
|
5010
|
+
for (const currentAlias of candidateAliases) {
|
|
5011
|
+
const currentParts = currentAlias.replace(/^crate(?:::)?/, "").split("::").filter(Boolean);
|
|
5012
|
+
if (superRelative) {
|
|
5013
|
+
if (currentParts.length > 0) {
|
|
5014
|
+
const parentParts = currentParts.slice(0, -1);
|
|
5015
|
+
const expanded2 = `crate${parentParts.length ? `::${parentParts.join("::")}` : ""}${tailAfter ? `::${tailAfter}` : ""}`.replace(/::+/g, "::").replace(/::$/, "");
|
|
5016
|
+
expansions.push(expanded2);
|
|
5017
|
+
}
|
|
5018
|
+
continue;
|
|
5019
|
+
}
|
|
5020
|
+
const expanded = `crate${currentParts.length ? `::${currentParts.join("::")}` : ""}${tailAfter ? `::${tailAfter}` : ""}`.replace(/::+/g, "::").replace(/::$/, "");
|
|
5021
|
+
expansions.push(expanded);
|
|
5022
|
+
}
|
|
5023
|
+
return uniqueBy(expansions, (item) => item);
|
|
5024
|
+
}
|
|
5025
|
+
function rustCrateRootPrefix(repoRelativePath) {
|
|
5026
|
+
if (!repoRelativePath) {
|
|
5027
|
+
return void 0;
|
|
5028
|
+
}
|
|
5029
|
+
const normalized = normalizeAlias(repoRelativePath);
|
|
5030
|
+
const idx = normalized.lastIndexOf("/src/");
|
|
5031
|
+
if (idx >= 0) {
|
|
5032
|
+
return normalized.slice(0, idx + "/src/".length);
|
|
4825
5033
|
}
|
|
4826
|
-
|
|
4827
|
-
|
|
4828
|
-
|
|
5034
|
+
if (normalized.startsWith("src/")) {
|
|
5035
|
+
return "src/";
|
|
5036
|
+
}
|
|
5037
|
+
return void 0;
|
|
5038
|
+
}
|
|
5039
|
+
function filterRustCandidatesToSameCrate(candidates, consumerPath) {
|
|
5040
|
+
const normalizedConsumer = consumerPath ? normalizeAlias(consumerPath) : "";
|
|
5041
|
+
const withoutSelf = normalizedConsumer ? candidates.filter((entry) => normalizeAlias(entry.repoRelativePath ?? "") !== normalizedConsumer) : candidates;
|
|
5042
|
+
if (withoutSelf.length <= 1) {
|
|
5043
|
+
return withoutSelf;
|
|
5044
|
+
}
|
|
5045
|
+
const cratePrefix = rustCrateRootPrefix(consumerPath);
|
|
5046
|
+
if (cratePrefix) {
|
|
5047
|
+
const sameCrate = withoutSelf.filter((entry) => normalizeAlias(entry.repoRelativePath ?? "").startsWith(cratePrefix));
|
|
5048
|
+
if (sameCrate.length > 0) {
|
|
5049
|
+
return sameCrate;
|
|
5050
|
+
}
|
|
5051
|
+
}
|
|
5052
|
+
if (normalizedConsumer) {
|
|
5053
|
+
let dir = path6.posix.dirname(normalizedConsumer);
|
|
5054
|
+
while (dir && dir !== "." && dir !== "/") {
|
|
5055
|
+
const prefix = `${dir}/`;
|
|
5056
|
+
const sameTree = withoutSelf.filter((entry) => normalizeAlias(entry.repoRelativePath ?? "").startsWith(prefix));
|
|
5057
|
+
if (sameTree.length > 0) {
|
|
5058
|
+
return sameTree;
|
|
5059
|
+
}
|
|
5060
|
+
const parent = path6.posix.dirname(dir);
|
|
5061
|
+
if (parent === dir) {
|
|
5062
|
+
break;
|
|
5063
|
+
}
|
|
5064
|
+
dir = parent;
|
|
5065
|
+
}
|
|
5066
|
+
}
|
|
5067
|
+
return withoutSelf;
|
|
5068
|
+
}
|
|
5069
|
+
function resolveRustAliasWithStripping(alias, lookup, consumerPath) {
|
|
5070
|
+
const segments = alias.split("::");
|
|
5071
|
+
while (segments.length > 0) {
|
|
5072
|
+
const candidate = segments.join("::");
|
|
5073
|
+
const matches = aliasMatchesExact(lookup, candidate);
|
|
5074
|
+
const filtered = matches.length > 0 ? filterRustCandidatesToSameCrate(matches, consumerPath) : [];
|
|
5075
|
+
if (filtered.length > 0) {
|
|
5076
|
+
return filtered;
|
|
5077
|
+
}
|
|
5078
|
+
if (candidate === "crate" || candidate === "self" || candidate === "super") {
|
|
5079
|
+
break;
|
|
5080
|
+
}
|
|
5081
|
+
segments.pop();
|
|
5082
|
+
}
|
|
5083
|
+
return [];
|
|
4829
5084
|
}
|
|
4830
5085
|
function luaSpecifierLooksLocal(specifier) {
|
|
4831
5086
|
return /^[A-Za-z_][A-Za-z0-9_]*(?:[./][A-Za-z_][A-Za-z0-9_]*)*$/.test(specifier);
|
|
4832
5087
|
}
|
|
4833
|
-
function resolveLuaModuleCandidates(specifier) {
|
|
5088
|
+
function resolveLuaModuleCandidates(specifier, repoRelativePath) {
|
|
4834
5089
|
const normalized = normalizeAlias(specifier.replace(/\./g, "/"));
|
|
4835
5090
|
if (!normalized) {
|
|
4836
5091
|
return [];
|
|
4837
5092
|
}
|
|
4838
|
-
|
|
5093
|
+
const bases = /* @__PURE__ */ new Set([normalized]);
|
|
5094
|
+
bases.add(`src/${normalized}`);
|
|
5095
|
+
bases.add(`lua/${normalized}`);
|
|
5096
|
+
if (repoRelativePath) {
|
|
5097
|
+
let dir = path6.posix.dirname(repoRelativePath);
|
|
5098
|
+
while (dir && dir !== "." && dir !== "/") {
|
|
5099
|
+
bases.add(`${dir}/${normalized}`);
|
|
5100
|
+
const parent = path6.posix.dirname(dir);
|
|
5101
|
+
if (parent === dir) {
|
|
5102
|
+
break;
|
|
5103
|
+
}
|
|
5104
|
+
dir = parent;
|
|
5105
|
+
}
|
|
5106
|
+
}
|
|
5107
|
+
const candidates = [];
|
|
5108
|
+
for (const base of bases) {
|
|
5109
|
+
candidates.push(`${base}.lua`);
|
|
5110
|
+
candidates.push(path6.posix.join(base, "init.lua"));
|
|
5111
|
+
}
|
|
5112
|
+
return uniqueBy(candidates, (item) => item);
|
|
4839
5113
|
}
|
|
4840
5114
|
function findImportCandidates(manifest, codeImport, lookup) {
|
|
4841
5115
|
const language = manifest.language ?? inferCodeLanguage(manifest.originalPath ?? manifest.storedPath, manifest.mimeType);
|
|
@@ -4863,7 +5137,7 @@ function findImportCandidates(manifest, codeImport, lookup) {
|
|
|
4863
5137
|
case "dart":
|
|
4864
5138
|
return repoRelativePath && dartSpecifierLooksLocal2(codeImport.specifier) ? repoPathMatches(lookup, ...importResolutionCandidates(repoRelativePath, codeImport.specifier, candidateExtensionsFor(language))) : aliasMatches(lookup, codeImport.specifier);
|
|
4865
5139
|
case "lua":
|
|
4866
|
-
return luaSpecifierLooksLocal(codeImport.specifier) ? repoPathMatches(lookup, ...resolveLuaModuleCandidates(codeImport.specifier)) : aliasMatches(lookup, codeImport.specifier, codeImport.specifier.replace(/\./g, "/"));
|
|
5140
|
+
return luaSpecifierLooksLocal(codeImport.specifier) ? repoPathMatches(lookup, ...resolveLuaModuleCandidates(codeImport.specifier, repoRelativePath)) : aliasMatches(lookup, codeImport.specifier, codeImport.specifier.replace(/\./g, "/"));
|
|
4867
5141
|
case "zig":
|
|
4868
5142
|
return repoRelativePath && (!codeImport.isExternal || codeImport.specifier.endsWith(".zig")) ? repoPathMatches(lookup, ...importResolutionCandidates(repoRelativePath, codeImport.specifier, candidateExtensionsFor(language))) : aliasMatches(lookup, codeImport.specifier);
|
|
4869
5143
|
case "php":
|
|
@@ -4882,8 +5156,15 @@ function findImportCandidates(manifest, codeImport, lookup) {
|
|
|
4882
5156
|
codeImport.specifier.replace(/\\/g, "/"),
|
|
4883
5157
|
stripCodeExtension2(codeImport.specifier.replace(/\\/g, "/"))
|
|
4884
5158
|
);
|
|
4885
|
-
case "rust":
|
|
4886
|
-
|
|
5159
|
+
case "rust": {
|
|
5160
|
+
for (const alias of [codeImport.specifier, ...resolveRustAliases(manifest, codeImport.specifier)]) {
|
|
5161
|
+
const matches = resolveRustAliasWithStripping(alias, lookup, repoRelativePath);
|
|
5162
|
+
if (matches.length > 0) {
|
|
5163
|
+
return matches;
|
|
5164
|
+
}
|
|
5165
|
+
}
|
|
5166
|
+
return [];
|
|
5167
|
+
}
|
|
4887
5168
|
case "c":
|
|
4888
5169
|
case "cpp":
|
|
4889
5170
|
return repoRelativePath && !codeImport.isExternal ? repoPathMatches(lookup, ...importResolutionCandidates(repoRelativePath, codeImport.specifier, candidateExtensionsFor(language))) : aliasMatches(lookup, codeImport.specifier);
|
|
@@ -4892,7 +5173,7 @@ function findImportCandidates(manifest, codeImport, lookup) {
|
|
|
4892
5173
|
}
|
|
4893
5174
|
}
|
|
4894
5175
|
function importLooksLocal(manifest, codeImport, candidates) {
|
|
4895
|
-
if (candidates.length
|
|
5176
|
+
if (candidates.length === 1) {
|
|
4896
5177
|
return true;
|
|
4897
5178
|
}
|
|
4898
5179
|
const language = manifest.language ?? inferCodeLanguage(manifest.originalPath ?? manifest.storedPath, manifest.mimeType);
|
|
@@ -5623,7 +5904,7 @@ async function extractEpubChapters(input) {
|
|
|
5623
5904
|
if (!markdown) {
|
|
5624
5905
|
continue;
|
|
5625
5906
|
}
|
|
5626
|
-
const chapterTitle = firstHtmlHeading(html) ||
|
|
5907
|
+
const chapterTitle = firstHtmlHeading(html) || item.href;
|
|
5627
5908
|
const normalizedTitle = normalizeWhitespace(chapterTitle);
|
|
5628
5909
|
if (!normalizedTitle || /^table of contents$/i.test(normalizedTitle)) {
|
|
5629
5910
|
continue;
|
|
@@ -6256,6 +6537,41 @@ async function appendWatchRun(rootDir, run) {
|
|
|
6256
6537
|
await appendJsonLine(paths.jobsLogPath, run);
|
|
6257
6538
|
}
|
|
6258
6539
|
|
|
6540
|
+
// src/markdown-ast.ts
|
|
6541
|
+
import { fromMarkdown } from "mdast-util-from-markdown";
|
|
6542
|
+
function parseMarkdownNodes(text) {
|
|
6543
|
+
try {
|
|
6544
|
+
const root = fromMarkdown(text);
|
|
6545
|
+
return Array.isArray(root.children) ? root.children : [];
|
|
6546
|
+
} catch {
|
|
6547
|
+
return [];
|
|
6548
|
+
}
|
|
6549
|
+
}
|
|
6550
|
+
function markdownNodeText(node) {
|
|
6551
|
+
if (node.type === "text" || node.type === "inlineCode" || node.type === "code") {
|
|
6552
|
+
return normalizeWhitespace(node.value ?? "");
|
|
6553
|
+
}
|
|
6554
|
+
if (node.type === "image") {
|
|
6555
|
+
return normalizeWhitespace(node.alt ?? "");
|
|
6556
|
+
}
|
|
6557
|
+
if (node.type === "break" || node.type === "thematicBreak") {
|
|
6558
|
+
return " ";
|
|
6559
|
+
}
|
|
6560
|
+
return normalizeWhitespace((node.children ?? []).map((child) => markdownNodeText(child)).join(" "));
|
|
6561
|
+
}
|
|
6562
|
+
function firstMarkdownHeading(text) {
|
|
6563
|
+
const nodes = parseMarkdownNodes(text);
|
|
6564
|
+
for (const node of nodes) {
|
|
6565
|
+
if (node.type === "heading") {
|
|
6566
|
+
const title = markdownNodeText(node).trim();
|
|
6567
|
+
if (title) {
|
|
6568
|
+
return title;
|
|
6569
|
+
}
|
|
6570
|
+
}
|
|
6571
|
+
}
|
|
6572
|
+
return void 0;
|
|
6573
|
+
}
|
|
6574
|
+
|
|
6259
6575
|
// src/source-classification.ts
|
|
6260
6576
|
import path9 from "path";
|
|
6261
6577
|
var ALL_SOURCE_CLASSES = ["first_party", "third_party", "resource", "generated"];
|
|
@@ -6610,7 +6926,7 @@ function inferKind(mimeType, filePath, detectionOptions = {}) {
|
|
|
6610
6926
|
if (mimeType === "text/csv" || mimeType === "text/tab-separated-values" || filePath.toLowerCase().endsWith(".csv") || filePath.toLowerCase().endsWith(".tsv")) {
|
|
6611
6927
|
return "csv";
|
|
6612
6928
|
}
|
|
6613
|
-
if (mimeType.startsWith("text/") || isStructuredTextMime(mimeType)
|
|
6929
|
+
if (mimeType.startsWith("text/") || isStructuredTextMime(mimeType)) {
|
|
6614
6930
|
return "text";
|
|
6615
6931
|
}
|
|
6616
6932
|
if (mimeType === "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" || filePath.toLowerCase().endsWith(".xlsx")) {
|
|
@@ -6685,8 +7001,7 @@ function titleFromText(fallback, content, filePath) {
|
|
|
6685
7001
|
return rstTitle;
|
|
6686
7002
|
}
|
|
6687
7003
|
}
|
|
6688
|
-
|
|
6689
|
-
return heading || fallback;
|
|
7004
|
+
return firstMarkdownHeading(content) ?? fallback;
|
|
6690
7005
|
}
|
|
6691
7006
|
function guessMimeType(target) {
|
|
6692
7007
|
if (isRstFilePath(target)) {
|
|
@@ -6696,125 +7011,30 @@ function guessMimeType(target) {
|
|
|
6696
7011
|
if (extension === ".ts" || extension === ".tsx" || extension === ".mts" || extension === ".cts") {
|
|
6697
7012
|
return "text/typescript";
|
|
6698
7013
|
}
|
|
6699
|
-
|
|
6700
|
-
|
|
6701
|
-
|
|
6702
|
-
|
|
6703
|
-
|
|
6704
|
-
return "text/plain";
|
|
6705
|
-
}
|
|
6706
|
-
return "application/octet-stream";
|
|
6707
|
-
}
|
|
6708
|
-
var KNOWN_TEXT_DOTFILE_NAMES = /* @__PURE__ */ new Set([
|
|
6709
|
-
".gitignore",
|
|
6710
|
-
".gitattributes",
|
|
6711
|
-
".gitkeep",
|
|
6712
|
-
".gitmodules",
|
|
6713
|
-
".editorconfig",
|
|
6714
|
-
".npmrc",
|
|
6715
|
-
".yarnrc",
|
|
6716
|
-
".prettierignore",
|
|
6717
|
-
".prettierrc",
|
|
6718
|
-
".dockerignore",
|
|
6719
|
-
".eslintignore",
|
|
6720
|
-
".eslintrc",
|
|
6721
|
-
".nvmrc",
|
|
6722
|
-
".node-version",
|
|
6723
|
-
".python-version",
|
|
6724
|
-
".ruby-version",
|
|
6725
|
-
".tool-versions"
|
|
6726
|
-
]);
|
|
6727
|
-
var KNOWN_TEXT_BASENAMES = /* @__PURE__ */ new Set([
|
|
6728
|
-
"readme",
|
|
6729
|
-
"license",
|
|
6730
|
-
"licence",
|
|
6731
|
-
"copying",
|
|
6732
|
-
"unlicense",
|
|
6733
|
-
"notice",
|
|
6734
|
-
"authors",
|
|
6735
|
-
"contributors",
|
|
6736
|
-
"patents",
|
|
6737
|
-
"maintainers",
|
|
6738
|
-
"owners",
|
|
6739
|
-
"codeowners",
|
|
6740
|
-
"changelog",
|
|
6741
|
-
"changes",
|
|
6742
|
-
"history",
|
|
6743
|
-
"news",
|
|
6744
|
-
"todo",
|
|
6745
|
-
"install",
|
|
6746
|
-
"dockerfile",
|
|
6747
|
-
"containerfile",
|
|
6748
|
-
"makefile",
|
|
6749
|
-
"gnumakefile",
|
|
6750
|
-
"rakefile",
|
|
6751
|
-
"gemfile",
|
|
6752
|
-
"procfile",
|
|
6753
|
-
"jenkinsfile",
|
|
6754
|
-
"vagrantfile",
|
|
6755
|
-
"brewfile",
|
|
6756
|
-
"go.mod",
|
|
6757
|
-
"go.sum",
|
|
6758
|
-
"go.work",
|
|
6759
|
-
"go.work.sum",
|
|
6760
|
-
"cargo.lock",
|
|
6761
|
-
"pipfile",
|
|
6762
|
-
"pipfile.lock",
|
|
6763
|
-
"poetry.lock",
|
|
6764
|
-
"uv.lock",
|
|
6765
|
-
"py.typed",
|
|
6766
|
-
"package-lock.json",
|
|
6767
|
-
"yarn.lock",
|
|
6768
|
-
"pnpm-lock.yaml",
|
|
6769
|
-
"composer.lock",
|
|
6770
|
-
"requirements.txt"
|
|
6771
|
-
]);
|
|
6772
|
-
var KNOWN_TEXT_BASENAME_PREFIXES = ["license", "licence", "copying", "unlicense", "readme", "changelog", "dockerfile", "containerfile"];
|
|
6773
|
-
var KNOWN_TEXT_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
6774
|
-
".toml",
|
|
6775
|
-
".lock",
|
|
6776
|
-
".tmpl",
|
|
6777
|
-
".template",
|
|
6778
|
-
".mustache",
|
|
6779
|
-
".hbs",
|
|
6780
|
-
".handlebars",
|
|
6781
|
-
".ejs",
|
|
6782
|
-
".njk",
|
|
6783
|
-
".liquid",
|
|
6784
|
-
".vim",
|
|
6785
|
-
".typed",
|
|
6786
|
-
".env",
|
|
6787
|
-
".properties",
|
|
6788
|
-
".ini",
|
|
6789
|
-
".cfg",
|
|
6790
|
-
".conf",
|
|
6791
|
-
".config",
|
|
6792
|
-
".bazel",
|
|
6793
|
-
".bzl",
|
|
6794
|
-
".bat",
|
|
6795
|
-
".cmd"
|
|
6796
|
-
]);
|
|
6797
|
-
function isKnownTextPath(target) {
|
|
6798
|
-
const basename = path12.basename(target).toLowerCase();
|
|
6799
|
-
if (KNOWN_TEXT_DOTFILE_NAMES.has(basename)) {
|
|
6800
|
-
return true;
|
|
6801
|
-
}
|
|
6802
|
-
if (basename === ".env" || basename.startsWith(".env.")) {
|
|
6803
|
-
return true;
|
|
6804
|
-
}
|
|
6805
|
-
if (KNOWN_TEXT_BASENAMES.has(basename)) {
|
|
6806
|
-
return true;
|
|
7014
|
+
return mime.lookup(target) || "application/octet-stream";
|
|
7015
|
+
}
|
|
7016
|
+
function refineBinaryKindWithBytes(absolutePath, currentKind, bytes) {
|
|
7017
|
+
if (currentKind !== "binary") {
|
|
7018
|
+
return currentKind;
|
|
6807
7019
|
}
|
|
6808
|
-
|
|
6809
|
-
|
|
6810
|
-
|
|
6811
|
-
|
|
7020
|
+
const sniffSlice = bytes.length > 4096 ? bytes.subarray(0, 4096) : bytes;
|
|
7021
|
+
return isText(absolutePath, sniffSlice) ? "text" : currentKind;
|
|
7022
|
+
}
|
|
7023
|
+
async function refineBinaryKindWithContentSniff(absolutePath, currentKind) {
|
|
7024
|
+
if (currentKind !== "binary") {
|
|
7025
|
+
return currentKind;
|
|
6812
7026
|
}
|
|
6813
|
-
|
|
6814
|
-
|
|
6815
|
-
|
|
7027
|
+
let handle;
|
|
7028
|
+
try {
|
|
7029
|
+
handle = await fs11.open(absolutePath, "r");
|
|
7030
|
+
const chunk = Buffer.alloc(4096);
|
|
7031
|
+
const { bytesRead } = await handle.read(chunk, 0, chunk.length, 0);
|
|
7032
|
+
return refineBinaryKindWithBytes(absolutePath, currentKind, chunk.subarray(0, bytesRead));
|
|
7033
|
+
} catch {
|
|
7034
|
+
return currentKind;
|
|
7035
|
+
} finally {
|
|
7036
|
+
await handle?.close().catch(() => void 0);
|
|
6816
7037
|
}
|
|
6817
|
-
return false;
|
|
6818
7038
|
}
|
|
6819
7039
|
function sourceGroupIdFor(prepared) {
|
|
6820
7040
|
const originKey = prepared.originType === "url" ? prepared.url ?? prepared.title : prepared.originalPath ?? prepared.title;
|
|
@@ -6945,8 +7165,7 @@ function normalizeRstExtractedText(content) {
|
|
|
6945
7165
|
}
|
|
6946
7166
|
function titleFromRst(fallback, content) {
|
|
6947
7167
|
const normalized = normalizeRstExtractedText(content);
|
|
6948
|
-
|
|
6949
|
-
return heading || fallback;
|
|
7168
|
+
return firstMarkdownHeading(normalized) ?? fallback;
|
|
6950
7169
|
}
|
|
6951
7170
|
function extractedTextForPlainSource(filePath, sourceKind, content) {
|
|
6952
7171
|
if (sourceKind === "text" && isRstFilePath(filePath)) {
|
|
@@ -7666,6 +7885,7 @@ async function collectDirectoryFiles(rootDir, inputDir, repoRoot, options) {
|
|
|
7666
7885
|
sourceKind = "chat_export";
|
|
7667
7886
|
}
|
|
7668
7887
|
}
|
|
7888
|
+
sourceKind = await refineBinaryKindWithContentSniff(absolutePath, sourceKind);
|
|
7669
7889
|
const sourceClass = sourceClassForRelativePath(relativePath, options);
|
|
7670
7890
|
if (!supportedDirectoryKind(sourceKind)) {
|
|
7671
7891
|
skipped.push({ path: toPosix(path12.relative(rootDir, absolutePath)), reason: `unsupported_kind:${sourceKind}` });
|
|
@@ -8287,7 +8507,7 @@ async function prepareFileInputs(rootDir, absoluteInput, repoRoot, sourceClass)
|
|
|
8287
8507
|
}
|
|
8288
8508
|
const mimeType = guessMimeType(absoluteInput);
|
|
8289
8509
|
const detectionOptions = await localCodeDetectionOptions(absoluteInput, payloadBytes);
|
|
8290
|
-
const sourceKind = inferKind(mimeType, absoluteInput, detectionOptions);
|
|
8510
|
+
const sourceKind = refineBinaryKindWithBytes(absoluteInput, inferKind(mimeType, absoluteInput, detectionOptions), payloadBytes);
|
|
8291
8511
|
const language = inferCodeLanguage(absoluteInput, mimeType, detectionOptions);
|
|
8292
8512
|
const storedExtension = path12.extname(absoluteInput) || `.${mime.extension(mimeType) || "bin"}`;
|
|
8293
8513
|
let title;
|
|
@@ -9071,6 +9291,7 @@ async function importInbox(rootDir, inputDir) {
|
|
|
9071
9291
|
sourceKind = "chat_export";
|
|
9072
9292
|
}
|
|
9073
9293
|
}
|
|
9294
|
+
sourceKind = await refineBinaryKindWithContentSniff(absolutePath, sourceKind);
|
|
9074
9295
|
if (!isSupportedInboxKind(sourceKind)) {
|
|
9075
9296
|
skipped.push({ path: toPosix(path12.relative(rootDir, absolutePath)), reason: `unsupported_kind:${sourceKind}` });
|
|
9076
9297
|
continue;
|
|
@@ -9268,8 +9489,55 @@ import { z as z7 } from "zod";
|
|
|
9268
9489
|
|
|
9269
9490
|
// src/analysis.ts
|
|
9270
9491
|
import path14 from "path";
|
|
9271
|
-
import
|
|
9492
|
+
import nlp2 from "compromise";
|
|
9272
9493
|
import { z as z2 } from "zod";
|
|
9494
|
+
|
|
9495
|
+
// src/tokenize.ts
|
|
9496
|
+
import nlp from "compromise";
|
|
9497
|
+
var CLOSED_CLASS_POS_SELECTOR = "#Determiner, #Preposition, #Conjunction, #Pronoun, #Auxiliary, #Copula";
|
|
9498
|
+
function splitTermToTokens(term, tokens) {
|
|
9499
|
+
for (const piece of term.split(/[^a-z0-9-]+/)) {
|
|
9500
|
+
const trimmed = piece.replace(/^-+|-+$/g, "");
|
|
9501
|
+
if (trimmed.length >= 2) {
|
|
9502
|
+
tokens.push(trimmed);
|
|
9503
|
+
}
|
|
9504
|
+
}
|
|
9505
|
+
}
|
|
9506
|
+
function tokenize(text) {
|
|
9507
|
+
const lower = text.toLowerCase();
|
|
9508
|
+
try {
|
|
9509
|
+
const terms = nlp(lower).terms().out("array");
|
|
9510
|
+
const tokens = [];
|
|
9511
|
+
for (const term of terms) {
|
|
9512
|
+
splitTermToTokens(term, tokens);
|
|
9513
|
+
}
|
|
9514
|
+
if (tokens.length > 0) {
|
|
9515
|
+
return tokens;
|
|
9516
|
+
}
|
|
9517
|
+
} catch {
|
|
9518
|
+
}
|
|
9519
|
+
return lower.match(/[a-z0-9][a-z0-9-]{1,}/g) ?? [];
|
|
9520
|
+
}
|
|
9521
|
+
function contentTokens(text, minLength = 4) {
|
|
9522
|
+
const lower = text.toLowerCase();
|
|
9523
|
+
const tokens = [];
|
|
9524
|
+
try {
|
|
9525
|
+
const contentDoc = nlp(lower).not(CLOSED_CLASS_POS_SELECTOR);
|
|
9526
|
+
const terms = contentDoc.terms().out("array");
|
|
9527
|
+
for (const term of terms) {
|
|
9528
|
+
splitTermToTokens(term, tokens);
|
|
9529
|
+
}
|
|
9530
|
+
} catch {
|
|
9531
|
+
}
|
|
9532
|
+
if (tokens.length === 0) {
|
|
9533
|
+
for (const piece of lower.match(/[a-z0-9][a-z0-9-]{1,}/g) ?? []) {
|
|
9534
|
+
tokens.push(piece);
|
|
9535
|
+
}
|
|
9536
|
+
}
|
|
9537
|
+
return tokens.filter((token) => token.length >= minLength);
|
|
9538
|
+
}
|
|
9539
|
+
|
|
9540
|
+
// src/analysis.ts
|
|
9273
9541
|
var ANALYSIS_FORMAT_VERSION = 7;
|
|
9274
9542
|
var sourceAnalysisSchema = z2.object({
|
|
9275
9543
|
title: z2.string().min(1),
|
|
@@ -9288,46 +9556,6 @@ var sourceAnalysisSchema = z2.object({
|
|
|
9288
9556
|
questions: z2.array(z2.string()).max(6).default([]),
|
|
9289
9557
|
tags: z2.array(z2.string()).max(5).default([])
|
|
9290
9558
|
});
|
|
9291
|
-
var STOPWORDS = /* @__PURE__ */ new Set([
|
|
9292
|
-
"about",
|
|
9293
|
-
"after",
|
|
9294
|
-
"also",
|
|
9295
|
-
"been",
|
|
9296
|
-
"being",
|
|
9297
|
-
"between",
|
|
9298
|
-
"both",
|
|
9299
|
-
"could",
|
|
9300
|
-
"does",
|
|
9301
|
-
"each",
|
|
9302
|
-
"from",
|
|
9303
|
-
"have",
|
|
9304
|
-
"into",
|
|
9305
|
-
"just",
|
|
9306
|
-
"more",
|
|
9307
|
-
"much",
|
|
9308
|
-
"only",
|
|
9309
|
-
"other",
|
|
9310
|
-
"over",
|
|
9311
|
-
"same",
|
|
9312
|
-
"some",
|
|
9313
|
-
"such",
|
|
9314
|
-
"than",
|
|
9315
|
-
"that",
|
|
9316
|
-
"their",
|
|
9317
|
-
"there",
|
|
9318
|
-
"these",
|
|
9319
|
-
"they",
|
|
9320
|
-
"this",
|
|
9321
|
-
"very",
|
|
9322
|
-
"what",
|
|
9323
|
-
"when",
|
|
9324
|
-
"where",
|
|
9325
|
-
"which",
|
|
9326
|
-
"while",
|
|
9327
|
-
"with",
|
|
9328
|
-
"would",
|
|
9329
|
-
"your"
|
|
9330
|
-
]);
|
|
9331
9559
|
var HEURISTIC_SECTION_SOURCE_KINDS = /* @__PURE__ */ new Map([
|
|
9332
9560
|
["transcript", "Transcript"],
|
|
9333
9561
|
["chat_export", "Messages"],
|
|
@@ -9336,20 +9564,33 @@ var HEURISTIC_SECTION_SOURCE_KINDS = /* @__PURE__ */ new Map([
|
|
|
9336
9564
|
]);
|
|
9337
9565
|
function extractTopTerms(text, count) {
|
|
9338
9566
|
const frequency = /* @__PURE__ */ new Map();
|
|
9339
|
-
for (const token of text
|
|
9340
|
-
if (STOPWORDS.has(token)) {
|
|
9341
|
-
continue;
|
|
9342
|
-
}
|
|
9567
|
+
for (const token of contentTokens(text)) {
|
|
9343
9568
|
frequency.set(token, (frequency.get(token) ?? 0) + 1);
|
|
9344
9569
|
}
|
|
9345
9570
|
return [...frequency.entries()].sort((left, right) => right[1] - left[1] || left[0].localeCompare(right[0])).slice(0, count).map(([token]) => token);
|
|
9346
9571
|
}
|
|
9347
9572
|
function extractEntities(text, count) {
|
|
9348
|
-
const
|
|
9349
|
-
|
|
9350
|
-
|
|
9351
|
-
|
|
9352
|
-
|
|
9573
|
+
const candidates = [];
|
|
9574
|
+
try {
|
|
9575
|
+
const doc = nlp2(text);
|
|
9576
|
+
const segments = [
|
|
9577
|
+
doc.match("#ProperNoun+").out("array"),
|
|
9578
|
+
doc.people().out("array"),
|
|
9579
|
+
doc.places().out("array"),
|
|
9580
|
+
doc.organizations().out("array"),
|
|
9581
|
+
doc.topics().out("array")
|
|
9582
|
+
];
|
|
9583
|
+
for (const segment of segments) {
|
|
9584
|
+
for (const term of segment) {
|
|
9585
|
+
const normalized = normalizeWhitespace(term);
|
|
9586
|
+
if (normalized) {
|
|
9587
|
+
candidates.push(normalized);
|
|
9588
|
+
}
|
|
9589
|
+
}
|
|
9590
|
+
}
|
|
9591
|
+
} catch {
|
|
9592
|
+
}
|
|
9593
|
+
return uniqueBy(candidates, (value) => value.toLowerCase()).slice(0, count);
|
|
9353
9594
|
}
|
|
9354
9595
|
function detectPolarity(text) {
|
|
9355
9596
|
if (/\b(no|not|never|cannot|can't|won't|without)\b/i.test(text)) {
|
|
@@ -9360,26 +9601,6 @@ function detectPolarity(text) {
|
|
|
9360
9601
|
}
|
|
9361
9602
|
return "neutral";
|
|
9362
9603
|
}
|
|
9363
|
-
function parseMarkdownNodes(text) {
|
|
9364
|
-
try {
|
|
9365
|
-
const root = fromMarkdown(text);
|
|
9366
|
-
return Array.isArray(root.children) ? root.children : [];
|
|
9367
|
-
} catch {
|
|
9368
|
-
return [];
|
|
9369
|
-
}
|
|
9370
|
-
}
|
|
9371
|
-
function markdownNodeText(node) {
|
|
9372
|
-
if (node.type === "text" || node.type === "inlineCode" || node.type === "code") {
|
|
9373
|
-
return normalizeWhitespace(node.value ?? "");
|
|
9374
|
-
}
|
|
9375
|
-
if (node.type === "image") {
|
|
9376
|
-
return normalizeWhitespace(node.alt ?? "");
|
|
9377
|
-
}
|
|
9378
|
-
if (node.type === "break" || node.type === "thematicBreak") {
|
|
9379
|
-
return " ";
|
|
9380
|
-
}
|
|
9381
|
-
return normalizeWhitespace((node.children ?? []).map((child) => markdownNodeText(child)).join(" "));
|
|
9382
|
-
}
|
|
9383
9604
|
function markdownNodesText(nodes) {
|
|
9384
9605
|
return normalizeWhitespace(nodes.map((node) => markdownNodeText(node)).join("\n"));
|
|
9385
9606
|
}
|
|
@@ -10654,7 +10875,7 @@ function filterGraphBySourceClass(graph, sourceClass) {
|
|
|
10654
10875
|
}
|
|
10655
10876
|
|
|
10656
10877
|
// src/graph-enrichment.ts
|
|
10657
|
-
var
|
|
10878
|
+
var STOPWORDS = /* @__PURE__ */ new Set([
|
|
10658
10879
|
"about",
|
|
10659
10880
|
"after",
|
|
10660
10881
|
"also",
|
|
@@ -10718,7 +10939,7 @@ function addFeature(bucket, reason, value) {
|
|
|
10718
10939
|
}
|
|
10719
10940
|
function themeTokens(value) {
|
|
10720
10941
|
return uniqueBy(
|
|
10721
|
-
normalizeValue(value).split(/[^a-z0-9]+/i).filter((token) => token.length >= 4 && !
|
|
10942
|
+
normalizeValue(value).split(/[^a-z0-9]+/i).filter((token) => token.length >= 4 && !STOPWORDS.has(token)),
|
|
10722
10943
|
(token) => token
|
|
10723
10944
|
).slice(0, 6);
|
|
10724
10945
|
}
|
|
@@ -11068,15 +11289,37 @@ function graphAdjacency(graph) {
|
|
|
11068
11289
|
}
|
|
11069
11290
|
return adjacency;
|
|
11070
11291
|
}
|
|
11292
|
+
var NODE_TYPE_PRIORITY = {
|
|
11293
|
+
concept: 6,
|
|
11294
|
+
entity: 5,
|
|
11295
|
+
source: 4,
|
|
11296
|
+
module: 3,
|
|
11297
|
+
symbol: 2,
|
|
11298
|
+
rationale: 1
|
|
11299
|
+
};
|
|
11300
|
+
function nodeTypePriority(type) {
|
|
11301
|
+
return NODE_TYPE_PRIORITY[type] ?? 0;
|
|
11302
|
+
}
|
|
11303
|
+
function compareLabelCandidates(left, right) {
|
|
11304
|
+
const priorityDelta = nodeTypePriority(right.type) - nodeTypePriority(left.type);
|
|
11305
|
+
if (priorityDelta !== 0) {
|
|
11306
|
+
return priorityDelta;
|
|
11307
|
+
}
|
|
11308
|
+
const degreeDelta = (right.degree ?? 0) - (left.degree ?? 0);
|
|
11309
|
+
if (degreeDelta !== 0) {
|
|
11310
|
+
return degreeDelta;
|
|
11311
|
+
}
|
|
11312
|
+
return left.id.localeCompare(right.id);
|
|
11313
|
+
}
|
|
11071
11314
|
function resolveNode(graph, target) {
|
|
11072
11315
|
const normalized = normalizeTarget(target);
|
|
11073
11316
|
const byId = nodeById(graph);
|
|
11074
11317
|
if (byId.has(target)) {
|
|
11075
11318
|
return byId.get(target);
|
|
11076
11319
|
}
|
|
11077
|
-
const
|
|
11078
|
-
if (
|
|
11079
|
-
return
|
|
11320
|
+
const labelMatches = graph.nodes.filter((node) => normalizeTarget(node.label) === normalized || normalizeTarget(node.id) === normalized);
|
|
11321
|
+
if (labelMatches.length) {
|
|
11322
|
+
return labelMatches.sort(compareLabelCandidates)[0];
|
|
11080
11323
|
}
|
|
11081
11324
|
const pages = graph.pages.map((page) => ({
|
|
11082
11325
|
page,
|
|
@@ -11085,7 +11328,7 @@ function resolveNode(graph, target) {
|
|
|
11085
11328
|
if (pages[0]) {
|
|
11086
11329
|
return primaryNodeForPage(graph, pages[0].page);
|
|
11087
11330
|
}
|
|
11088
|
-
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
|
|
11331
|
+
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;
|
|
11089
11332
|
}
|
|
11090
11333
|
function communityLabel(graph, communityId) {
|
|
11091
11334
|
if (!communityId) {
|
|
@@ -13331,8 +13574,7 @@ function getDatabaseSync() {
|
|
|
13331
13574
|
return builtin.DatabaseSync;
|
|
13332
13575
|
}
|
|
13333
13576
|
function toFtsQuery(query) {
|
|
13334
|
-
|
|
13335
|
-
return tokens.join(" OR ");
|
|
13577
|
+
return tokenize(query).join(" OR ");
|
|
13336
13578
|
}
|
|
13337
13579
|
function normalizeKind(value) {
|
|
13338
13580
|
return value === "index" || value === "source" || value === "module" || value === "concept" || value === "entity" || value === "output" || value === "insight" || value === "graph_report" || value === "community_summary" ? value : void 0;
|
|
@@ -13761,7 +14003,7 @@ async function resolveImageGenerationProvider(rootDir) {
|
|
|
13761
14003
|
if (!providerConfig) {
|
|
13762
14004
|
throw new Error(`No provider configured with id "${preferredProviderId}" for task "imageProvider".`);
|
|
13763
14005
|
}
|
|
13764
|
-
const { createProvider: createProvider2 } = await import("./registry-
|
|
14006
|
+
const { createProvider: createProvider2 } = await import("./registry-W6ZFRI73.js");
|
|
13765
14007
|
return createProvider2(preferredProviderId, providerConfig, rootDir);
|
|
13766
14008
|
}
|
|
13767
14009
|
async function generateOutputArtifacts(rootDir, input) {
|
|
@@ -17860,6 +18102,10 @@ function extractClaimSectionLines(content) {
|
|
|
17860
18102
|
}
|
|
17861
18103
|
return found ? claimLines : null;
|
|
17862
18104
|
}
|
|
18105
|
+
function isClaimPlaceholderBullet(line) {
|
|
18106
|
+
const trimmed = line.trim();
|
|
18107
|
+
return /^-\s+No\s+claims\s+extracted\.?$/i.test(trimmed);
|
|
18108
|
+
}
|
|
17863
18109
|
function structuralLintFindings(_rootDir, paths, graph, schemas, manifests, sourceProjects) {
|
|
17864
18110
|
const manifestMap = new Map(manifests.map((manifest) => [manifest.sourceId, manifest]));
|
|
17865
18111
|
const pageMap2 = new Map(graph.pages.map((page) => [page.id, page]));
|
|
@@ -17907,7 +18153,9 @@ function structuralLintFindings(_rootDir, paths, graph, schemas, manifests, sour
|
|
|
17907
18153
|
const content = await fs19.readFile(absolutePath, "utf8");
|
|
17908
18154
|
const claimLines = extractClaimSectionLines(content);
|
|
17909
18155
|
if (claimLines !== null) {
|
|
17910
|
-
const uncited = claimLines.filter(
|
|
18156
|
+
const uncited = claimLines.filter(
|
|
18157
|
+
(line) => line.startsWith("- ") && !line.includes("[source:") && !isClaimPlaceholderBullet(line)
|
|
18158
|
+
);
|
|
17911
18159
|
if (uncited.length) {
|
|
17912
18160
|
findings.push({
|
|
17913
18161
|
severity: "warning",
|
|
@@ -18022,7 +18270,7 @@ async function bootstrapDemo(rootDir, input) {
|
|
|
18022
18270
|
}
|
|
18023
18271
|
|
|
18024
18272
|
// src/mcp.ts
|
|
18025
|
-
var SERVER_VERSION = "0.6.
|
|
18273
|
+
var SERVER_VERSION = "0.6.8";
|
|
18026
18274
|
async function createMcpServer(rootDir) {
|
|
18027
18275
|
const server = new McpServer({
|
|
18028
18276
|
name: "swarmvault",
|
|
@@ -18034,10 +18282,10 @@ async function createMcpServer(rootDir) {
|
|
|
18034
18282
|
{
|
|
18035
18283
|
description: "Return the current SwarmVault workspace paths and high-level counts."
|
|
18036
18284
|
},
|
|
18037
|
-
async () => {
|
|
18285
|
+
safeHandler(async () => {
|
|
18038
18286
|
const info = await getWorkspaceInfo(rootDir);
|
|
18039
18287
|
return asToolText(info);
|
|
18040
|
-
}
|
|
18288
|
+
})
|
|
18041
18289
|
);
|
|
18042
18290
|
server.registerTool(
|
|
18043
18291
|
"search_pages",
|
|
@@ -18048,10 +18296,10 @@ async function createMcpServer(rootDir) {
|
|
|
18048
18296
|
limit: z8.number().int().min(1).max(25).optional().describe("Maximum number of results")
|
|
18049
18297
|
}
|
|
18050
18298
|
},
|
|
18051
|
-
async ({ query, limit }) => {
|
|
18299
|
+
safeHandler(async ({ query, limit }) => {
|
|
18052
18300
|
const results = await searchVault(rootDir, query, limit ?? 5);
|
|
18053
18301
|
return asToolText(results);
|
|
18054
|
-
}
|
|
18302
|
+
})
|
|
18055
18303
|
);
|
|
18056
18304
|
server.registerTool(
|
|
18057
18305
|
"read_page",
|
|
@@ -18061,13 +18309,13 @@ async function createMcpServer(rootDir) {
|
|
|
18061
18309
|
path: z8.string().min(1).describe("Path relative to wiki/, for example sources/example.md")
|
|
18062
18310
|
}
|
|
18063
18311
|
},
|
|
18064
|
-
async ({ path: relativePath }) => {
|
|
18312
|
+
safeHandler(async ({ path: relativePath }) => {
|
|
18065
18313
|
const page = await readPage(rootDir, relativePath);
|
|
18066
18314
|
if (!page) {
|
|
18067
18315
|
return asToolError(`Page not found: ${relativePath}`);
|
|
18068
18316
|
}
|
|
18069
18317
|
return asToolText(page);
|
|
18070
|
-
}
|
|
18318
|
+
})
|
|
18071
18319
|
);
|
|
18072
18320
|
server.registerTool(
|
|
18073
18321
|
"list_sources",
|
|
@@ -18077,10 +18325,10 @@ async function createMcpServer(rootDir) {
|
|
|
18077
18325
|
limit: z8.number().int().min(1).max(100).optional().describe("Maximum number of manifests to return")
|
|
18078
18326
|
}
|
|
18079
18327
|
},
|
|
18080
|
-
async ({ limit }) => {
|
|
18328
|
+
safeHandler(async ({ limit }) => {
|
|
18081
18329
|
const manifests = await listManifests(rootDir);
|
|
18082
18330
|
return asToolText(limit ? manifests.slice(0, limit) : manifests);
|
|
18083
|
-
}
|
|
18331
|
+
})
|
|
18084
18332
|
);
|
|
18085
18333
|
server.registerTool(
|
|
18086
18334
|
"query_graph",
|
|
@@ -18092,22 +18340,22 @@ async function createMcpServer(rootDir) {
|
|
|
18092
18340
|
budget: z8.number().int().min(3).max(50).optional().describe("Maximum nodes to summarize")
|
|
18093
18341
|
}
|
|
18094
18342
|
},
|
|
18095
|
-
async ({ question, traversal, budget }) => {
|
|
18343
|
+
safeHandler(async ({ question, traversal, budget }) => {
|
|
18096
18344
|
const result = await queryGraphVault(rootDir, question, {
|
|
18097
18345
|
traversal,
|
|
18098
18346
|
budget
|
|
18099
18347
|
});
|
|
18100
18348
|
return asToolText(result);
|
|
18101
|
-
}
|
|
18349
|
+
})
|
|
18102
18350
|
);
|
|
18103
18351
|
server.registerTool(
|
|
18104
18352
|
"graph_report",
|
|
18105
18353
|
{
|
|
18106
18354
|
description: "Return the machine-readable graph report and trust artifact."
|
|
18107
18355
|
},
|
|
18108
|
-
async () => {
|
|
18356
|
+
safeHandler(async () => {
|
|
18109
18357
|
return asToolText(await readGraphReport(rootDir) ?? { error: "Graph report not found. Run `swarmvault compile` first." });
|
|
18110
|
-
}
|
|
18358
|
+
})
|
|
18111
18359
|
);
|
|
18112
18360
|
server.registerTool(
|
|
18113
18361
|
"get_node",
|
|
@@ -18117,9 +18365,9 @@ async function createMcpServer(rootDir) {
|
|
|
18117
18365
|
target: z8.string().min(1).describe("Node or page label/id")
|
|
18118
18366
|
}
|
|
18119
18367
|
},
|
|
18120
|
-
async ({ target }) => {
|
|
18368
|
+
safeHandler(async ({ target }) => {
|
|
18121
18369
|
return asToolText(await explainGraphVault(rootDir, target));
|
|
18122
|
-
}
|
|
18370
|
+
})
|
|
18123
18371
|
);
|
|
18124
18372
|
server.registerTool(
|
|
18125
18373
|
"get_hyperedges",
|
|
@@ -18130,9 +18378,9 @@ async function createMcpServer(rootDir) {
|
|
|
18130
18378
|
limit: z8.number().int().min(1).max(50).optional().describe("Maximum hyperedges to return")
|
|
18131
18379
|
}
|
|
18132
18380
|
},
|
|
18133
|
-
async ({ target, limit }) => {
|
|
18381
|
+
safeHandler(async ({ target, limit }) => {
|
|
18134
18382
|
return asToolText(await listGraphHyperedges(rootDir, target, limit ?? 25));
|
|
18135
|
-
}
|
|
18383
|
+
})
|
|
18136
18384
|
);
|
|
18137
18385
|
server.registerTool(
|
|
18138
18386
|
"get_neighbors",
|
|
@@ -18142,10 +18390,10 @@ async function createMcpServer(rootDir) {
|
|
|
18142
18390
|
target: z8.string().min(1).describe("Node or page label/id")
|
|
18143
18391
|
}
|
|
18144
18392
|
},
|
|
18145
|
-
async ({ target }) => {
|
|
18393
|
+
safeHandler(async ({ target }) => {
|
|
18146
18394
|
const explanation = await explainGraphVault(rootDir, target);
|
|
18147
18395
|
return asToolText(explanation.neighbors);
|
|
18148
|
-
}
|
|
18396
|
+
})
|
|
18149
18397
|
);
|
|
18150
18398
|
server.registerTool(
|
|
18151
18399
|
"shortest_path",
|
|
@@ -18156,9 +18404,9 @@ async function createMcpServer(rootDir) {
|
|
|
18156
18404
|
to: z8.string().min(1).describe("End node/page label or id")
|
|
18157
18405
|
}
|
|
18158
18406
|
},
|
|
18159
|
-
async ({ from, to }) => {
|
|
18407
|
+
safeHandler(async ({ from, to }) => {
|
|
18160
18408
|
return asToolText(await pathGraphVault(rootDir, from, to));
|
|
18161
|
-
}
|
|
18409
|
+
})
|
|
18162
18410
|
);
|
|
18163
18411
|
server.registerTool(
|
|
18164
18412
|
"god_nodes",
|
|
@@ -18168,9 +18416,9 @@ async function createMcpServer(rootDir) {
|
|
|
18168
18416
|
limit: z8.number().int().min(1).max(25).optional().describe("Maximum nodes to return")
|
|
18169
18417
|
}
|
|
18170
18418
|
},
|
|
18171
|
-
async ({ limit }) => {
|
|
18419
|
+
safeHandler(async ({ limit }) => {
|
|
18172
18420
|
return asToolText(await listGodNodes(rootDir, limit ?? 10));
|
|
18173
|
-
}
|
|
18421
|
+
})
|
|
18174
18422
|
);
|
|
18175
18423
|
server.registerTool(
|
|
18176
18424
|
"query_vault",
|
|
@@ -18182,14 +18430,14 @@ async function createMcpServer(rootDir) {
|
|
|
18182
18430
|
format: z8.enum(["markdown", "report", "slides", "chart", "image"]).optional().describe("Output format")
|
|
18183
18431
|
}
|
|
18184
18432
|
},
|
|
18185
|
-
async ({ question, save, format }) => {
|
|
18433
|
+
safeHandler(async ({ question, save, format }) => {
|
|
18186
18434
|
const result = await queryVault(rootDir, {
|
|
18187
18435
|
question,
|
|
18188
18436
|
save: save ?? true,
|
|
18189
18437
|
format
|
|
18190
18438
|
});
|
|
18191
18439
|
return asToolText(result);
|
|
18192
|
-
}
|
|
18440
|
+
})
|
|
18193
18441
|
);
|
|
18194
18442
|
server.registerTool(
|
|
18195
18443
|
"ingest_input",
|
|
@@ -18199,10 +18447,10 @@ async function createMcpServer(rootDir) {
|
|
|
18199
18447
|
input: z8.string().min(1).describe("Local path or URL to ingest")
|
|
18200
18448
|
}
|
|
18201
18449
|
},
|
|
18202
|
-
async ({ input }) => {
|
|
18450
|
+
safeHandler(async ({ input }) => {
|
|
18203
18451
|
const result = await ingestInputDetailed(rootDir, input);
|
|
18204
18452
|
return asToolText(result);
|
|
18205
|
-
}
|
|
18453
|
+
})
|
|
18206
18454
|
);
|
|
18207
18455
|
server.registerTool(
|
|
18208
18456
|
"compile_vault",
|
|
@@ -18212,20 +18460,20 @@ async function createMcpServer(rootDir) {
|
|
|
18212
18460
|
approve: z8.boolean().optional().describe("Stage a review bundle without applying active page changes")
|
|
18213
18461
|
}
|
|
18214
18462
|
},
|
|
18215
|
-
async ({ approve }) => {
|
|
18463
|
+
safeHandler(async ({ approve }) => {
|
|
18216
18464
|
const result = await compileVault(rootDir, { approve: approve ?? false });
|
|
18217
18465
|
return asToolText(result);
|
|
18218
|
-
}
|
|
18466
|
+
})
|
|
18219
18467
|
);
|
|
18220
18468
|
server.registerTool(
|
|
18221
18469
|
"lint_vault",
|
|
18222
18470
|
{
|
|
18223
18471
|
description: "Run anti-drift and vault health checks."
|
|
18224
18472
|
},
|
|
18225
|
-
async () => {
|
|
18473
|
+
safeHandler(async () => {
|
|
18226
18474
|
const findings = await lintVault(rootDir);
|
|
18227
18475
|
return asToolText(findings);
|
|
18228
|
-
}
|
|
18476
|
+
})
|
|
18229
18477
|
);
|
|
18230
18478
|
server.registerResource(
|
|
18231
18479
|
"swarmvault-config",
|
|
@@ -18396,6 +18644,17 @@ function asToolError(message) {
|
|
|
18396
18644
|
]
|
|
18397
18645
|
};
|
|
18398
18646
|
}
|
|
18647
|
+
function safeHandler(handler) {
|
|
18648
|
+
return async (args) => {
|
|
18649
|
+
try {
|
|
18650
|
+
return await handler(args);
|
|
18651
|
+
} catch (error) {
|
|
18652
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
18653
|
+
console.error(`[swarmvault-mcp] tool handler failed: ${message}`);
|
|
18654
|
+
return asToolError(message);
|
|
18655
|
+
}
|
|
18656
|
+
};
|
|
18657
|
+
}
|
|
18399
18658
|
function asTextResource(uri, text) {
|
|
18400
18659
|
return {
|
|
18401
18660
|
contents: [
|
|
@@ -18643,13 +18902,22 @@ async function serveSchedules(rootDir, pollMs = 3e4) {
|
|
|
18643
18902
|
}
|
|
18644
18903
|
running = true;
|
|
18645
18904
|
try {
|
|
18646
|
-
|
|
18905
|
+
let schedules = [];
|
|
18906
|
+
try {
|
|
18907
|
+
schedules = await listSchedules(rootDir);
|
|
18908
|
+
} catch (error) {
|
|
18909
|
+
console.error(`[swarmvault-schedule] failed to list schedules: ${error instanceof Error ? error.message : String(error)}`);
|
|
18910
|
+
}
|
|
18647
18911
|
const due = schedules.filter((item) => item.enabled).filter((item) => !item.nextRunAt || Date.parse(item.nextRunAt) <= Date.now()).sort((left, right) => (left.nextRunAt ?? "").localeCompare(right.nextRunAt ?? ""));
|
|
18648
18912
|
for (const schedule of due) {
|
|
18649
18913
|
if (closed) {
|
|
18650
18914
|
break;
|
|
18651
18915
|
}
|
|
18652
|
-
|
|
18916
|
+
try {
|
|
18917
|
+
await runSchedule(rootDir, schedule.jobId);
|
|
18918
|
+
} catch (error) {
|
|
18919
|
+
console.error(`[swarmvault-schedule] job ${schedule.jobId} crashed: ${error instanceof Error ? error.message : String(error)}`);
|
|
18920
|
+
}
|
|
18653
18921
|
}
|
|
18654
18922
|
} finally {
|
|
18655
18923
|
running = false;
|
|
@@ -20592,10 +20860,6 @@ var MAX_BACKOFF_MS = 3e4;
|
|
|
20592
20860
|
var BACKOFF_THRESHOLD = 3;
|
|
20593
20861
|
var CRITICAL_THRESHOLD = 10;
|
|
20594
20862
|
var REPO_WATCH_IGNORES = /* @__PURE__ */ new Set([".git", ".venv"]);
|
|
20595
|
-
function withinRoot3(rootPath, targetPath) {
|
|
20596
|
-
const relative = path27.relative(rootPath, targetPath);
|
|
20597
|
-
return relative === "" || !relative.startsWith("..") && !path27.isAbsolute(relative);
|
|
20598
|
-
}
|
|
20599
20863
|
function hasIgnoredRepoSegment(baseDir, targetPath) {
|
|
20600
20864
|
const relativePath = path27.relative(baseDir, targetPath);
|
|
20601
20865
|
if (!relativePath || relativePath.startsWith("..")) {
|
|
@@ -20765,11 +21029,11 @@ async function watchVault(rootDir, options = {}) {
|
|
|
20765
21029
|
interval: 100,
|
|
20766
21030
|
ignored: (targetPath) => {
|
|
20767
21031
|
const absolutePath = path27.resolve(targetPath);
|
|
20768
|
-
const primaryTarget = watchTargets.filter((watchTarget) =>
|
|
21032
|
+
const primaryTarget = watchTargets.filter((watchTarget) => isPathWithin(watchTarget, absolutePath)).sort((left, right) => right.length - left.length)[0] ?? null;
|
|
20769
21033
|
if (!primaryTarget) {
|
|
20770
21034
|
return false;
|
|
20771
21035
|
}
|
|
20772
|
-
if (primaryTarget !== inboxWatchRoot && ignoredRoots.some((ignoreRoot) =>
|
|
21036
|
+
if (primaryTarget !== inboxWatchRoot && ignoredRoots.some((ignoreRoot) => isPathWithin(ignoreRoot, absolutePath))) {
|
|
20773
21037
|
return true;
|
|
20774
21038
|
}
|
|
20775
21039
|
return hasIgnoredRepoSegment(primaryTarget, absolutePath);
|
|
@@ -20953,7 +21217,7 @@ async function watchVault(rootDir, options = {}) {
|
|
|
20953
21217
|
}
|
|
20954
21218
|
};
|
|
20955
21219
|
const reasonForPath = (targetPath) => {
|
|
20956
|
-
const baseDir = watchTargets.filter((watchTarget) =>
|
|
21220
|
+
const baseDir = watchTargets.filter((watchTarget) => isPathWithin(watchTarget, path27.resolve(targetPath))).sort((left, right) => right.length - left.length)[0] ?? paths.inboxDir;
|
|
20957
21221
|
return path27.relative(baseDir, targetPath) || ".";
|
|
20958
21222
|
};
|
|
20959
21223
|
watcher.on("add", (filePath) => schedule(`add:${reasonForPath(filePath)}`)).on("change", (filePath) => schedule(`change:${reasonForPath(filePath)}`)).on("unlink", (filePath) => schedule(`unlink:${reasonForPath(filePath)}`)).on("addDir", (dirPath) => schedule(`addDir:${reasonForPath(dirPath)}`)).on("unlinkDir", (dirPath) => schedule(`unlinkDir:${reasonForPath(dirPath)}`)).on("error", (caught) => schedule(`error:${caught instanceof Error ? caught.message : String(caught)}`));
|
|
@@ -21115,14 +21379,9 @@ async function startGraphServer(rootDir, port, options = {}) {
|
|
|
21115
21379
|
response.end(JSON.stringify({ error: "Missing explain target." }));
|
|
21116
21380
|
return;
|
|
21117
21381
|
}
|
|
21118
|
-
|
|
21119
|
-
|
|
21120
|
-
|
|
21121
|
-
response.end(JSON.stringify(result));
|
|
21122
|
-
} catch (error) {
|
|
21123
|
-
response.writeHead(404, { "content-type": "application/json" });
|
|
21124
|
-
response.end(JSON.stringify({ error: error instanceof Error ? error.message : `Could not resolve graph target: ${target2}` }));
|
|
21125
|
-
}
|
|
21382
|
+
const result = await explainGraphVault(rootDir, target2);
|
|
21383
|
+
response.writeHead(200, { "content-type": "application/json" });
|
|
21384
|
+
response.end(JSON.stringify(result));
|
|
21126
21385
|
return;
|
|
21127
21386
|
}
|
|
21128
21387
|
if (url.pathname === "/api/search") {
|
|
@@ -21261,7 +21520,8 @@ async function startGraphServer(rootDir, port, options = {}) {
|
|
|
21261
21520
|
const message = error instanceof Error ? error.message : String(error);
|
|
21262
21521
|
console.error(`[viewer] ${request.method ?? "GET"} ${url.pathname} failed: ${message}`);
|
|
21263
21522
|
if (!response.headersSent) {
|
|
21264
|
-
|
|
21523
|
+
const status = /not found|could not resolve|cannot resolve/i.test(message) ? 404 : 500;
|
|
21524
|
+
response.writeHead(status, { "content-type": "application/json" });
|
|
21265
21525
|
response.end(JSON.stringify({ error: message }));
|
|
21266
21526
|
} else {
|
|
21267
21527
|
response.end();
|