@swarmvaultai/engine 0.6.7 → 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 +894 -624
- package/dist/registry-W6ZFRI73.js +12 -0
- package/package.json +2 -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 -->";
|
|
@@ -188,7 +219,7 @@ async function readJsonWithWarnings(filePath, fallback, label) {
|
|
|
188
219
|
async function installClaudeHook(rootDir) {
|
|
189
220
|
const settingsPath = path.join(rootDir, ".claude", "settings.json");
|
|
190
221
|
const scriptPath = path.join(rootDir, ".claude", "hooks", "swarmvault-graph-first.js");
|
|
191
|
-
await writeOwnedFile(scriptPath,
|
|
222
|
+
await writeOwnedFile(scriptPath, await readBuiltHook("claude.js"), true);
|
|
192
223
|
await ensureDir(path.dirname(settingsPath));
|
|
193
224
|
const { data: settings, warnings } = await readJsonWithWarnings(settingsPath, {}, ".claude/settings.json");
|
|
194
225
|
if (warnings.length > 0 && await fileExists(settingsPath)) {
|
|
@@ -218,361 +249,10 @@ async function installClaudeHook(rootDir) {
|
|
|
218
249
|
`, "utf8");
|
|
219
250
|
return { path: settingsPath, warnings: [] };
|
|
220
251
|
}
|
|
221
|
-
function buildClaudeHookScript() {
|
|
222
|
-
return `#!/usr/bin/env node
|
|
223
|
-
import crypto from "node:crypto";
|
|
224
|
-
import fs from "node:fs/promises";
|
|
225
|
-
import os from "node:os";
|
|
226
|
-
import path from "node:path";
|
|
227
|
-
|
|
228
|
-
${markerStateSnippet("claude").trim()}
|
|
229
|
-
|
|
230
|
-
async function readInput() {
|
|
231
|
-
let body = "";
|
|
232
|
-
for await (const chunk of process.stdin) {
|
|
233
|
-
body += chunk;
|
|
234
|
-
}
|
|
235
|
-
if (!body.trim()) {
|
|
236
|
-
return {};
|
|
237
|
-
}
|
|
238
|
-
try {
|
|
239
|
-
return JSON.parse(body);
|
|
240
|
-
} catch {
|
|
241
|
-
return {};
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
function emit(value) {
|
|
246
|
-
process.stdout.write(\`\${JSON.stringify(value)}\\n\`);
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
const mode = process.argv[2] ?? "";
|
|
250
|
-
const input = await readInput();
|
|
251
|
-
const cwd = resolveInputCwd(input);
|
|
252
|
-
const reportNote = "SwarmVault graph report exists at wiki/graph/report.md. Read it before broad grep/glob searching.";
|
|
253
|
-
|
|
254
|
-
if (!(await hasReport(cwd))) {
|
|
255
|
-
emit({});
|
|
256
|
-
process.exit(0);
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
if (mode === "session-start") {
|
|
260
|
-
await resetSession(cwd);
|
|
261
|
-
emit({
|
|
262
|
-
hookSpecificOutput: {
|
|
263
|
-
hookEventName: "SessionStart",
|
|
264
|
-
additionalContext: reportNote
|
|
265
|
-
}
|
|
266
|
-
});
|
|
267
|
-
process.exit(0);
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
const toolName = resolveToolName(input);
|
|
271
|
-
if (collectCandidatePaths(input).some((value) => isReportPath(value, cwd))) {
|
|
272
|
-
await markReportRead(cwd);
|
|
273
|
-
emit({});
|
|
274
|
-
process.exit(0);
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
if (isBroadSearchTool(toolName) && !(await hasSeenReport(cwd))) {
|
|
278
|
-
emit({
|
|
279
|
-
hookSpecificOutput: {
|
|
280
|
-
hookEventName: "PreToolUse",
|
|
281
|
-
additionalContext: reportNote
|
|
282
|
-
}
|
|
283
|
-
});
|
|
284
|
-
process.exit(0);
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
emit({});
|
|
288
|
-
`;
|
|
289
|
-
}
|
|
290
|
-
function markerStateSnippet(agentKey) {
|
|
291
|
-
return `
|
|
292
|
-
function markerState(cwd) {
|
|
293
|
-
const hash = crypto.createHash("sha256").update(cwd).digest("hex");
|
|
294
|
-
const dir = path.join(os.tmpdir(), "swarmvault-agent-hooks", "${agentKey}", hash);
|
|
295
|
-
return {
|
|
296
|
-
dir,
|
|
297
|
-
markerPath: path.join(dir, "report-read")
|
|
298
|
-
};
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
function isReportPath(value, cwd) {
|
|
302
|
-
if (typeof value !== "string" || value.length === 0) {
|
|
303
|
-
return false;
|
|
304
|
-
}
|
|
305
|
-
const reportSuffix = path.join("wiki", "graph", "report.md");
|
|
306
|
-
const normalized = value.replaceAll("\\\\", "/");
|
|
307
|
-
const reportNormalized = reportSuffix.replaceAll("\\\\", "/");
|
|
308
|
-
if (normalized.endsWith(reportNormalized)) {
|
|
309
|
-
return true;
|
|
310
|
-
}
|
|
311
|
-
return path.resolve(cwd, value) === path.resolve(cwd, reportSuffix);
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
function collectCandidatePaths(node, acc = []) {
|
|
315
|
-
if (typeof node === "string") {
|
|
316
|
-
acc.push(node);
|
|
317
|
-
return acc;
|
|
318
|
-
}
|
|
319
|
-
if (!node || typeof node !== "object") {
|
|
320
|
-
return acc;
|
|
321
|
-
}
|
|
322
|
-
if (Array.isArray(node)) {
|
|
323
|
-
for (const item of node) {
|
|
324
|
-
collectCandidatePaths(item, acc);
|
|
325
|
-
}
|
|
326
|
-
return acc;
|
|
327
|
-
}
|
|
328
|
-
for (const [key, value] of Object.entries(node)) {
|
|
329
|
-
if (["path", "filePath", "file_path", "paths", "target", "targets"].includes(key)) {
|
|
330
|
-
collectCandidatePaths(value, acc);
|
|
331
|
-
continue;
|
|
332
|
-
}
|
|
333
|
-
collectCandidatePaths(value, acc);
|
|
334
|
-
}
|
|
335
|
-
return acc;
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
function resolveInputCwd(input) {
|
|
339
|
-
return path.resolve(
|
|
340
|
-
input?.cwd ??
|
|
341
|
-
input?.directory ??
|
|
342
|
-
input?.workspace?.cwd ??
|
|
343
|
-
input?.toolInput?.cwd ??
|
|
344
|
-
process.cwd()
|
|
345
|
-
);
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
function resolveToolName(input) {
|
|
349
|
-
return String(input?.toolName ?? input?.tool_name ?? input?.tool?.name ?? input?.name ?? "");
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
async function hasReport(cwd) {
|
|
353
|
-
try {
|
|
354
|
-
await fs.access(path.join(cwd, "wiki", "graph", "report.md"));
|
|
355
|
-
return true;
|
|
356
|
-
} catch {
|
|
357
|
-
return false;
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
async function markReportRead(cwd) {
|
|
362
|
-
const state = markerState(cwd);
|
|
363
|
-
await fs.mkdir(state.dir, { recursive: true });
|
|
364
|
-
await fs.writeFile(state.markerPath, "seen\\n", "utf8");
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
async function hasSeenReport(cwd) {
|
|
368
|
-
const state = markerState(cwd);
|
|
369
|
-
try {
|
|
370
|
-
await fs.access(state.markerPath);
|
|
371
|
-
return true;
|
|
372
|
-
} catch {
|
|
373
|
-
return false;
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
async function resetSession(cwd) {
|
|
378
|
-
const state = markerState(cwd);
|
|
379
|
-
await fs.rm(state.dir, { recursive: true, force: true });
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
function isBroadSearchTool(toolName) {
|
|
383
|
-
return /grep|glob|search|find/i.test(toolName);
|
|
384
|
-
}
|
|
385
|
-
`;
|
|
386
|
-
}
|
|
387
|
-
function buildGeminiHookScript() {
|
|
388
|
-
return `#!/usr/bin/env node
|
|
389
|
-
import crypto from "node:crypto";
|
|
390
|
-
import fs from "node:fs/promises";
|
|
391
|
-
import os from "node:os";
|
|
392
|
-
import path from "node:path";
|
|
393
|
-
|
|
394
|
-
${markerStateSnippet("gemini").trim()}
|
|
395
|
-
|
|
396
|
-
async function readInput() {
|
|
397
|
-
let body = "";
|
|
398
|
-
for await (const chunk of process.stdin) {
|
|
399
|
-
body += chunk;
|
|
400
|
-
}
|
|
401
|
-
if (!body.trim()) {
|
|
402
|
-
return {};
|
|
403
|
-
}
|
|
404
|
-
try {
|
|
405
|
-
return JSON.parse(body);
|
|
406
|
-
} catch {
|
|
407
|
-
return {};
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
function emit(value) {
|
|
412
|
-
process.stdout.write(\`\${JSON.stringify(value)}\\n\`);
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
const mode = process.argv[2] ?? "";
|
|
416
|
-
const input = await readInput();
|
|
417
|
-
const cwd = resolveInputCwd(input);
|
|
418
|
-
const reportNote = "SwarmVault graph report exists at wiki/graph/report.md. Read it before broad grep/glob searching.";
|
|
419
|
-
|
|
420
|
-
if (!(await hasReport(cwd))) {
|
|
421
|
-
emit({});
|
|
422
|
-
process.exit(0);
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
if (mode === "session-start") {
|
|
426
|
-
await resetSession(cwd);
|
|
427
|
-
emit({
|
|
428
|
-
systemMessage: reportNote,
|
|
429
|
-
hookSpecificOutput: {
|
|
430
|
-
hookEventName: "SessionStart",
|
|
431
|
-
additionalContext: "SwarmVault graph report: wiki/graph/report.md"
|
|
432
|
-
}
|
|
433
|
-
});
|
|
434
|
-
process.exit(0);
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
const toolName = resolveToolName(input);
|
|
438
|
-
if (collectCandidatePaths(input).some((value) => isReportPath(value, cwd))) {
|
|
439
|
-
await markReportRead(cwd);
|
|
440
|
-
emit({});
|
|
441
|
-
process.exit(0);
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
if (isBroadSearchTool(toolName) && !(await hasSeenReport(cwd))) {
|
|
445
|
-
emit({ systemMessage: reportNote });
|
|
446
|
-
process.exit(0);
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
emit({});
|
|
450
|
-
`;
|
|
451
|
-
}
|
|
452
|
-
function buildCopilotHookScript() {
|
|
453
|
-
return `#!/usr/bin/env node
|
|
454
|
-
import crypto from "node:crypto";
|
|
455
|
-
import fs from "node:fs/promises";
|
|
456
|
-
import os from "node:os";
|
|
457
|
-
import path from "node:path";
|
|
458
|
-
|
|
459
|
-
${markerStateSnippet("copilot").trim()}
|
|
460
|
-
|
|
461
|
-
async function readInput() {
|
|
462
|
-
let body = "";
|
|
463
|
-
for await (const chunk of process.stdin) {
|
|
464
|
-
body += chunk;
|
|
465
|
-
}
|
|
466
|
-
if (!body.trim()) {
|
|
467
|
-
return {};
|
|
468
|
-
}
|
|
469
|
-
try {
|
|
470
|
-
return JSON.parse(body);
|
|
471
|
-
} catch {
|
|
472
|
-
return {};
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
function emit(value) {
|
|
477
|
-
if (value !== undefined) {
|
|
478
|
-
process.stdout.write(\`\${JSON.stringify(value)}\\n\`);
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
const mode = process.argv[2] ?? "";
|
|
483
|
-
const input = await readInput();
|
|
484
|
-
const cwd = resolveInputCwd(input);
|
|
485
|
-
const reportNote = "SwarmVault graph report exists at wiki/graph/report.md. Read it before broad grep/glob searching.";
|
|
486
|
-
|
|
487
|
-
if (!(await hasReport(cwd))) {
|
|
488
|
-
emit({});
|
|
489
|
-
process.exit(0);
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
if (mode === "session-start") {
|
|
493
|
-
await resetSession(cwd);
|
|
494
|
-
emit({});
|
|
495
|
-
process.exit(0);
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
const toolName = resolveToolName(input);
|
|
499
|
-
if (collectCandidatePaths(input).some((value) => isReportPath(value, cwd))) {
|
|
500
|
-
await markReportRead(cwd);
|
|
501
|
-
emit({});
|
|
502
|
-
process.exit(0);
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
if (isBroadSearchTool(toolName) && !(await hasSeenReport(cwd))) {
|
|
506
|
-
emit({
|
|
507
|
-
permissionDecision: "deny",
|
|
508
|
-
permissionDecisionReason: reportNote
|
|
509
|
-
});
|
|
510
|
-
process.exit(0);
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
emit({});
|
|
514
|
-
`;
|
|
515
|
-
}
|
|
516
|
-
function buildOpenCodePlugin() {
|
|
517
|
-
return `import path from "node:path";
|
|
518
|
-
|
|
519
|
-
const reportRelativePath = path.join("wiki", "graph", "report.md");
|
|
520
|
-
|
|
521
|
-
export const name = "swarmvault-graph-first";
|
|
522
|
-
|
|
523
|
-
export default async function swarmvaultGraphFirst({ client }) {
|
|
524
|
-
let reportSeen = false;
|
|
525
|
-
|
|
526
|
-
async function hasReport(cwd) {
|
|
527
|
-
try {
|
|
528
|
-
await Bun.file(path.join(cwd, reportRelativePath)).arrayBuffer();
|
|
529
|
-
return true;
|
|
530
|
-
} catch {
|
|
531
|
-
return false;
|
|
532
|
-
}
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
async function note(message) {
|
|
536
|
-
if (client?.app?.log) {
|
|
537
|
-
await client.app.log({
|
|
538
|
-
level: "info",
|
|
539
|
-
message
|
|
540
|
-
});
|
|
541
|
-
}
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
return {
|
|
545
|
-
async "session.created"(input) {
|
|
546
|
-
reportSeen = false;
|
|
547
|
-
const cwd = input?.session?.cwd ?? process.cwd();
|
|
548
|
-
if (await hasReport(cwd)) {
|
|
549
|
-
await note("SwarmVault graph report exists. Read wiki/graph/report.md before broad workspace searching.");
|
|
550
|
-
}
|
|
551
|
-
},
|
|
552
|
-
async "tool.execute.before"(input) {
|
|
553
|
-
const cwd = input?.session?.cwd ?? process.cwd();
|
|
554
|
-
if (!(await hasReport(cwd))) {
|
|
555
|
-
return;
|
|
556
|
-
}
|
|
557
|
-
|
|
558
|
-
const argsText = JSON.stringify(input?.args ?? {});
|
|
559
|
-
if (argsText.includes("wiki/graph/report.md")) {
|
|
560
|
-
reportSeen = true;
|
|
561
|
-
return;
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
if (!reportSeen && ["glob", "grep"].includes(String(input?.tool ?? ""))) {
|
|
565
|
-
await note("SwarmVault graph report exists. Read wiki/graph/report.md before broad workspace searching.");
|
|
566
|
-
}
|
|
567
|
-
}
|
|
568
|
-
};
|
|
569
|
-
}
|
|
570
|
-
`;
|
|
571
|
-
}
|
|
572
252
|
async function installGeminiHook(rootDir) {
|
|
573
253
|
const settingsPath = path.join(rootDir, ".gemini", "settings.json");
|
|
574
254
|
const scriptPath = path.join(rootDir, ".gemini", "hooks", "swarmvault-graph-first.js");
|
|
575
|
-
await writeOwnedFile(scriptPath,
|
|
255
|
+
await writeOwnedFile(scriptPath, await readBuiltHook("gemini.js"), true);
|
|
576
256
|
const { data: settings, warnings } = await readJsonWithWarnings(settingsPath, {}, ".gemini/settings.json");
|
|
577
257
|
if (warnings.length > 0 && await fileExists(settingsPath)) {
|
|
578
258
|
return { paths: [settingsPath, scriptPath], warnings };
|
|
@@ -639,7 +319,7 @@ async function installCopilotHook(rootDir) {
|
|
|
639
319
|
const hooksDir = path.join(rootDir, ".github", "hooks");
|
|
640
320
|
const scriptPath = path.join(hooksDir, "swarmvault-graph-first.js");
|
|
641
321
|
const configPath = path.join(hooksDir, "swarmvault-graph-first.json");
|
|
642
|
-
await writeOwnedFile(scriptPath,
|
|
322
|
+
await writeOwnedFile(scriptPath, await readBuiltHook("copilot.js"), true);
|
|
643
323
|
const config = {
|
|
644
324
|
version: copilotHookVersion,
|
|
645
325
|
hooks: {
|
|
@@ -670,7 +350,7 @@ async function installCopilotHook(rootDir) {
|
|
|
670
350
|
}
|
|
671
351
|
async function installOpenCodeHook(rootDir) {
|
|
672
352
|
const pluginPath = path.join(rootDir, ".opencode", "plugins", "swarmvault-graph-first.js");
|
|
673
|
-
await writeOwnedFile(pluginPath,
|
|
353
|
+
await writeOwnedFile(pluginPath, await readBuiltHook("opencode.js"));
|
|
674
354
|
return { paths: [pluginPath], warnings: [] };
|
|
675
355
|
}
|
|
676
356
|
function stableKeyForAgent(rootDir, agent) {
|
|
@@ -1376,7 +1056,6 @@ function buildBenchmarkArtifact(input) {
|
|
|
1376
1056
|
reductionRatio,
|
|
1377
1057
|
sampleQuestions: input.questions,
|
|
1378
1058
|
perQuestion,
|
|
1379
|
-
questionResults: perQuestion,
|
|
1380
1059
|
summary
|
|
1381
1060
|
};
|
|
1382
1061
|
}
|
|
@@ -2182,104 +1861,431 @@ function treeSitterCompatibilityDiagnostic(language, error) {
|
|
|
2182
1861
|
column: 1
|
|
2183
1862
|
};
|
|
2184
1863
|
}
|
|
2185
|
-
function
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
return [];
|
|
1864
|
+
function flattenPythonDottedName(node) {
|
|
1865
|
+
if (!node) {
|
|
1866
|
+
return "";
|
|
2189
1867
|
}
|
|
2190
|
-
return
|
|
2191
|
-
const [specifier, alias] = item.split(/\s+as\s+/i);
|
|
2192
|
-
return {
|
|
2193
|
-
specifier: specifier.trim(),
|
|
2194
|
-
importedSymbols: [],
|
|
2195
|
-
namespaceImport: alias?.trim(),
|
|
2196
|
-
isExternal: !specifier.trim().startsWith("."),
|
|
2197
|
-
reExport: false
|
|
2198
|
-
};
|
|
2199
|
-
});
|
|
1868
|
+
return node.namedChildren.filter((child) => child?.type === "identifier").map((child) => child.text.trim()).filter(Boolean).join(".");
|
|
2200
1869
|
}
|
|
2201
|
-
function
|
|
2202
|
-
|
|
2203
|
-
|
|
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) {
|
|
1922
|
+
return [];
|
|
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) {
|
|
2204
1933
|
return [];
|
|
2205
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
|
+
}
|
|
2206
1962
|
return [
|
|
2207
1963
|
{
|
|
2208
|
-
specifier
|
|
2209
|
-
importedSymbols:
|
|
2210
|
-
isExternal: !
|
|
1964
|
+
specifier,
|
|
1965
|
+
importedSymbols: symbols,
|
|
1966
|
+
isExternal: !specifier.startsWith("."),
|
|
2211
1967
|
reExport: false
|
|
2212
1968
|
}
|
|
2213
1969
|
];
|
|
2214
1970
|
}
|
|
2215
|
-
function parseGoImport(
|
|
2216
|
-
|
|
2217
|
-
|
|
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) {
|
|
2218
2003
|
return void 0;
|
|
2219
2004
|
}
|
|
2220
2005
|
return {
|
|
2221
|
-
specifier
|
|
2006
|
+
specifier,
|
|
2222
2007
|
importedSymbols: [],
|
|
2223
|
-
namespaceImport:
|
|
2224
|
-
isExternal: !
|
|
2008
|
+
namespaceImport: !dotImport && !blankImport ? alias : void 0,
|
|
2009
|
+
isExternal: !specifier.startsWith("."),
|
|
2225
2010
|
reExport: false
|
|
2226
2011
|
};
|
|
2227
2012
|
}
|
|
2228
|
-
function
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
)
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
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));
|
|
2243
2034
|
}
|
|
2244
|
-
function
|
|
2245
|
-
|
|
2246
|
-
|
|
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();
|
|
2247
2173
|
return {
|
|
2248
|
-
specifier
|
|
2174
|
+
specifier,
|
|
2249
2175
|
importedSymbols: symbolName ? [symbolName] : [],
|
|
2250
2176
|
isExternal: true,
|
|
2251
2177
|
reExport: false
|
|
2252
2178
|
};
|
|
2253
2179
|
}
|
|
2254
|
-
function
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2180
|
+
function flattenKotlinIdentifier(node) {
|
|
2181
|
+
if (!node) {
|
|
2182
|
+
return "";
|
|
2183
|
+
}
|
|
2184
|
+
if (node.type === "simple_identifier") {
|
|
2185
|
+
return node.text.trim();
|
|
2258
2186
|
}
|
|
2259
|
-
|
|
2260
|
-
|
|
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);
|
|
2261
2192
|
if (!specifier) {
|
|
2262
2193
|
return void 0;
|
|
2263
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;
|
|
2264
2198
|
return {
|
|
2265
|
-
specifier,
|
|
2266
|
-
importedSymbols: [],
|
|
2267
|
-
namespaceImport:
|
|
2268
|
-
isExternal:
|
|
2199
|
+
specifier: hasWildcard ? `${specifier}.*` : specifier,
|
|
2200
|
+
importedSymbols: hasWildcard ? ["*"] : [],
|
|
2201
|
+
namespaceImport: aliasName || void 0,
|
|
2202
|
+
isExternal: true,
|
|
2269
2203
|
reExport: false
|
|
2270
2204
|
};
|
|
2271
2205
|
}
|
|
2272
|
-
function
|
|
2273
|
-
|
|
2274
|
-
|
|
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) {
|
|
2275
2222
|
return [];
|
|
2276
2223
|
}
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
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
|
+
];
|
|
2283
2289
|
}
|
|
2284
2290
|
function bashCommandName(commandNode) {
|
|
2285
2291
|
if (!commandNode) {
|
|
@@ -2389,66 +2395,176 @@ function parseZigImport(node) {
|
|
|
2389
2395
|
if (!importCall || nodeText(findNamedChild(importCall, "builtin_identifier") ?? importCall.namedChildren.at(0) ?? null) !== "@import") {
|
|
2390
2396
|
return void 0;
|
|
2391
2397
|
}
|
|
2392
|
-
const stringNode = importCall.descendantsOfType("string_content").find((item) => item !== null);
|
|
2393
|
-
const specifier = stringNode?.text.trim();
|
|
2398
|
+
const stringNode = importCall.descendantsOfType("string_content").find((item) => item !== null);
|
|
2399
|
+
const specifier = stringNode?.text.trim();
|
|
2400
|
+
if (!specifier) {
|
|
2401
|
+
return void 0;
|
|
2402
|
+
}
|
|
2403
|
+
return {
|
|
2404
|
+
specifier,
|
|
2405
|
+
importedSymbols: [],
|
|
2406
|
+
isExternal: !specifier.endsWith(".zig") && !specifier.includes("/") && !specifier.startsWith("."),
|
|
2407
|
+
reExport: false
|
|
2408
|
+
};
|
|
2409
|
+
}
|
|
2410
|
+
function flattenCSharpQualifiedName(node) {
|
|
2411
|
+
if (!node) {
|
|
2412
|
+
return "";
|
|
2413
|
+
}
|
|
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) {
|
|
2440
|
+
return void 0;
|
|
2441
|
+
}
|
|
2442
|
+
return {
|
|
2443
|
+
specifier,
|
|
2444
|
+
importedSymbols: [],
|
|
2445
|
+
namespaceImport: aliasName,
|
|
2446
|
+
isExternal: !specifier.startsWith("."),
|
|
2447
|
+
reExport: false
|
|
2448
|
+
};
|
|
2449
|
+
}
|
|
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();
|
|
2475
|
+
}
|
|
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 {
|
|
2492
|
+
return void 0;
|
|
2493
|
+
}
|
|
2494
|
+
if (prefix && specifier) {
|
|
2495
|
+
specifier = `${prefix}\\${specifier}`;
|
|
2496
|
+
}
|
|
2394
2497
|
if (!specifier) {
|
|
2395
2498
|
return void 0;
|
|
2396
2499
|
}
|
|
2397
2500
|
return {
|
|
2398
2501
|
specifier,
|
|
2399
2502
|
importedSymbols: [],
|
|
2400
|
-
|
|
2503
|
+
namespaceImport: aliasName,
|
|
2504
|
+
isExternal: true,
|
|
2401
2505
|
reExport: false
|
|
2402
2506
|
};
|
|
2403
2507
|
}
|
|
2404
|
-
function
|
|
2405
|
-
const
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
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;
|
|
2414
2524
|
}
|
|
2415
|
-
const
|
|
2416
|
-
|
|
2417
|
-
|
|
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
|
+
}
|
|
2418
2533
|
}
|
|
2419
|
-
return
|
|
2420
|
-
specifier: match[1].trim(),
|
|
2421
|
-
importedSymbols: [],
|
|
2422
|
-
isExternal: !match[1].trim().startsWith("."),
|
|
2423
|
-
reExport: false
|
|
2424
|
-
};
|
|
2425
|
-
}
|
|
2426
|
-
function parsePhpUse(text) {
|
|
2427
|
-
const cleaned = text.trim().replace(/^use\s+/, "").replace(/;$/, "");
|
|
2428
|
-
return cleaned.split(",").map((item) => item.trim()).filter(Boolean).map((item) => {
|
|
2429
|
-
const aliasMatch = item.match(/^(.+?)\s+as\s+([A-Za-z_]\w*)$/i);
|
|
2430
|
-
const specifier = (aliasMatch ? aliasMatch[1] : item).trim();
|
|
2431
|
-
return {
|
|
2432
|
-
specifier,
|
|
2433
|
-
importedSymbols: [],
|
|
2434
|
-
namespaceImport: aliasMatch?.[2],
|
|
2435
|
-
isExternal: !specifier.startsWith("."),
|
|
2436
|
-
reExport: false
|
|
2437
|
-
};
|
|
2438
|
-
});
|
|
2534
|
+
return results;
|
|
2439
2535
|
}
|
|
2440
|
-
function parseCppInclude(
|
|
2441
|
-
const
|
|
2442
|
-
|
|
2443
|
-
|
|
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
|
+
}
|
|
2444
2566
|
}
|
|
2445
|
-
|
|
2446
|
-
return {
|
|
2447
|
-
specifier,
|
|
2448
|
-
importedSymbols: [],
|
|
2449
|
-
isExternal: match[1].startsWith("<"),
|
|
2450
|
-
reExport: false
|
|
2451
|
-
};
|
|
2567
|
+
return void 0;
|
|
2452
2568
|
}
|
|
2453
2569
|
function rubyStringContent(node) {
|
|
2454
2570
|
if (!node) {
|
|
@@ -2459,7 +2575,8 @@ function rubyStringContent(node) {
|
|
|
2459
2575
|
}
|
|
2460
2576
|
function normalizePowerShellDotSourceSpecifier(raw) {
|
|
2461
2577
|
const unquoted = raw.replace(/^['"]+|['"]+$/g, "").trim();
|
|
2462
|
-
|
|
2578
|
+
const withoutScriptRoot = unquoted.replace(/^\$PSScriptRoot(?:[\\/]+|$)/i, "./");
|
|
2579
|
+
return withoutScriptRoot.replace(/\\/g, "/");
|
|
2463
2580
|
}
|
|
2464
2581
|
function parsePowerShellImport(commandNode) {
|
|
2465
2582
|
const commandName = commandNode.descendantsOfType(["command_name", "command_name_expr"]).find((item) => item !== null)?.text.trim();
|
|
@@ -2813,11 +2930,11 @@ function pythonCodeAnalysis(manifest, rootNode, diagnostics) {
|
|
|
2813
2930
|
continue;
|
|
2814
2931
|
}
|
|
2815
2932
|
if (child.type === "import_statement") {
|
|
2816
|
-
imports.push(...parsePythonImportStatement(child
|
|
2933
|
+
imports.push(...parsePythonImportStatement(child));
|
|
2817
2934
|
continue;
|
|
2818
2935
|
}
|
|
2819
2936
|
if (child.type === "import_from_statement") {
|
|
2820
|
-
imports.push(...parsePythonFromImportStatement(child
|
|
2937
|
+
imports.push(...parsePythonFromImportStatement(child));
|
|
2821
2938
|
continue;
|
|
2822
2939
|
}
|
|
2823
2940
|
if (child.type === "class_definition") {
|
|
@@ -2872,7 +2989,7 @@ function goCodeAnalysis(manifest, rootNode, diagnostics) {
|
|
|
2872
2989
|
}
|
|
2873
2990
|
if (child.type === "import_declaration") {
|
|
2874
2991
|
for (const spec of child.descendantsOfType("import_spec")) {
|
|
2875
|
-
const parsed = spec ? parseGoImport(spec
|
|
2992
|
+
const parsed = spec ? parseGoImport(spec) : void 0;
|
|
2876
2993
|
if (parsed) {
|
|
2877
2994
|
imports.push(parsed);
|
|
2878
2995
|
}
|
|
@@ -2946,7 +3063,7 @@ function rustCodeAnalysis(manifest, rootNode, diagnostics) {
|
|
|
2946
3063
|
continue;
|
|
2947
3064
|
}
|
|
2948
3065
|
if (child.type === "use_declaration") {
|
|
2949
|
-
imports.push(
|
|
3066
|
+
imports.push(...parseRustUseDeclaration(child));
|
|
2950
3067
|
continue;
|
|
2951
3068
|
}
|
|
2952
3069
|
if (child.type === "mod_item") {
|
|
@@ -3025,11 +3142,15 @@ function javaCodeAnalysis(manifest, rootNode, diagnostics) {
|
|
|
3025
3142
|
continue;
|
|
3026
3143
|
}
|
|
3027
3144
|
if (child.type === "package_declaration") {
|
|
3028
|
-
|
|
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
|
+
}
|
|
3029
3150
|
continue;
|
|
3030
3151
|
}
|
|
3031
3152
|
if (child.type === "import_declaration") {
|
|
3032
|
-
imports.push(parseJavaImport(child
|
|
3153
|
+
imports.push(parseJavaImport(child));
|
|
3033
3154
|
continue;
|
|
3034
3155
|
}
|
|
3035
3156
|
const name = extractIdentifier(child.childForFieldName("name"));
|
|
@@ -3117,7 +3238,7 @@ function kotlinCodeAnalysis(manifest, rootNode, diagnostics) {
|
|
|
3117
3238
|
}
|
|
3118
3239
|
if (child.type === "import_list") {
|
|
3119
3240
|
for (const importNode of child.descendantsOfType("import_header").filter((item) => item !== null)) {
|
|
3120
|
-
const parsed = parseKotlinImport(importNode
|
|
3241
|
+
const parsed = parseKotlinImport(importNode);
|
|
3121
3242
|
if (parsed) {
|
|
3122
3243
|
imports.push(parsed);
|
|
3123
3244
|
}
|
|
@@ -3207,7 +3328,7 @@ function scalaCodeAnalysis(manifest, rootNode, diagnostics) {
|
|
|
3207
3328
|
continue;
|
|
3208
3329
|
}
|
|
3209
3330
|
if (child.type === "import_declaration") {
|
|
3210
|
-
imports.push(...parseScalaImport(child
|
|
3331
|
+
imports.push(...parseScalaImport(child));
|
|
3211
3332
|
continue;
|
|
3212
3333
|
}
|
|
3213
3334
|
if (child.type === "function_definition") {
|
|
@@ -3388,7 +3509,7 @@ function csharpCodeAnalysis(manifest, rootNode, diagnostics) {
|
|
|
3388
3509
|
continue;
|
|
3389
3510
|
}
|
|
3390
3511
|
if (child.type === "using_directive") {
|
|
3391
|
-
const parsed = parseCSharpUsing(child
|
|
3512
|
+
const parsed = parseCSharpUsing(child);
|
|
3392
3513
|
if (parsed) {
|
|
3393
3514
|
imports.push(parsed);
|
|
3394
3515
|
}
|
|
@@ -3480,7 +3601,7 @@ function phpCodeAnalysis(manifest, rootNode, diagnostics) {
|
|
|
3480
3601
|
continue;
|
|
3481
3602
|
}
|
|
3482
3603
|
if (child.type === "namespace_use_declaration") {
|
|
3483
|
-
imports.push(...parsePhpUse(child
|
|
3604
|
+
imports.push(...parsePhpUse(child));
|
|
3484
3605
|
continue;
|
|
3485
3606
|
}
|
|
3486
3607
|
const name = extractIdentifier(child.childForFieldName("name"));
|
|
@@ -3704,7 +3825,7 @@ function cFamilyCodeAnalysis(manifest, language, rootNode, diagnostics) {
|
|
|
3704
3825
|
continue;
|
|
3705
3826
|
}
|
|
3706
3827
|
if (child.type === "preproc_include") {
|
|
3707
|
-
const parsed = parseCppInclude(child
|
|
3828
|
+
const parsed = parseCppInclude(child);
|
|
3708
3829
|
if (parsed) {
|
|
3709
3830
|
imports.push(parsed);
|
|
3710
3831
|
}
|
|
@@ -4638,15 +4759,36 @@ async function readNearestGoModulePath(startPath, cache) {
|
|
|
4638
4759
|
current = parent;
|
|
4639
4760
|
}
|
|
4640
4761
|
}
|
|
4641
|
-
function
|
|
4642
|
-
const withoutExt = stripCodeExtension2(normalizeAlias(repoRelativePath));
|
|
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
|
+
};
|
|
4643
4780
|
const srcIdx = withoutExt.lastIndexOf("/src/");
|
|
4644
|
-
|
|
4645
|
-
|
|
4646
|
-
|
|
4647
|
-
|
|
4781
|
+
if (srcIdx >= 0) {
|
|
4782
|
+
push(withoutExt.slice(srcIdx + "/src/".length));
|
|
4783
|
+
}
|
|
4784
|
+
if (withoutExt.startsWith("src/")) {
|
|
4785
|
+
push(withoutExt.slice("src/".length));
|
|
4786
|
+
}
|
|
4787
|
+
const segments = withoutExt.split("/").filter(Boolean);
|
|
4788
|
+
for (let start = 0; start < segments.length; start += 1) {
|
|
4789
|
+
push(segments.slice(start).join("/"));
|
|
4648
4790
|
}
|
|
4649
|
-
return
|
|
4791
|
+
return uniqueBy(result.filter(Boolean), (item) => item);
|
|
4650
4792
|
}
|
|
4651
4793
|
function candidateExtensionsFor(language) {
|
|
4652
4794
|
switch (language) {
|
|
@@ -4722,7 +4864,9 @@ async function buildCodeIndex(rootDir, manifests, analyses) {
|
|
|
4722
4864
|
break;
|
|
4723
4865
|
case "rust":
|
|
4724
4866
|
if (repoRelativePath) {
|
|
4725
|
-
|
|
4867
|
+
for (const alias of rustModuleAliases(repoRelativePath)) {
|
|
4868
|
+
recordAlias(aliases, alias);
|
|
4869
|
+
}
|
|
4726
4870
|
}
|
|
4727
4871
|
break;
|
|
4728
4872
|
case "go": {
|
|
@@ -4833,6 +4977,9 @@ function aliasMatches(lookup, ...aliases) {
|
|
|
4833
4977
|
(entry) => entry.sourceId
|
|
4834
4978
|
);
|
|
4835
4979
|
}
|
|
4980
|
+
function aliasMatchesExact(lookup, alias) {
|
|
4981
|
+
return lookup.byAlias.get(normalizeAlias(alias)) ?? [];
|
|
4982
|
+
}
|
|
4836
4983
|
function repoPathMatches(lookup, ...repoPaths) {
|
|
4837
4984
|
return uniqueBy(
|
|
4838
4985
|
repoPaths.map((repoPath) => lookup.byRepoPath.get(normalizeAlias(repoPath))).filter((entry) => Boolean(entry)),
|
|
@@ -4850,30 +4997,119 @@ function resolvePythonRelativeAliases(repoRelativePath, specifier) {
|
|
|
4850
4997
|
}
|
|
4851
4998
|
function resolveRustAliases(manifest, specifier) {
|
|
4852
4999
|
const repoRelativePath = manifest.repoRelativePath ? normalizeAlias(manifest.repoRelativePath) : "";
|
|
4853
|
-
|
|
4854
|
-
if (!specifier.startsWith("self::") && !specifier.startsWith("super::")) {
|
|
5000
|
+
if (!specifier.startsWith("self::") && !specifier.startsWith("super::") && specifier !== "self" && specifier !== "super") {
|
|
4855
5001
|
return [specifier];
|
|
4856
5002
|
}
|
|
4857
|
-
|
|
5003
|
+
const candidateAliases = repoRelativePath ? rustModuleAliases(repoRelativePath) : [];
|
|
5004
|
+
if (candidateAliases.length === 0) {
|
|
4858
5005
|
return [];
|
|
4859
5006
|
}
|
|
4860
|
-
const
|
|
4861
|
-
|
|
4862
|
-
|
|
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);
|
|
4863
5033
|
}
|
|
4864
|
-
|
|
4865
|
-
|
|
4866
|
-
|
|
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 [];
|
|
4867
5084
|
}
|
|
4868
5085
|
function luaSpecifierLooksLocal(specifier) {
|
|
4869
5086
|
return /^[A-Za-z_][A-Za-z0-9_]*(?:[./][A-Za-z_][A-Za-z0-9_]*)*$/.test(specifier);
|
|
4870
5087
|
}
|
|
4871
|
-
function resolveLuaModuleCandidates(specifier) {
|
|
5088
|
+
function resolveLuaModuleCandidates(specifier, repoRelativePath) {
|
|
4872
5089
|
const normalized = normalizeAlias(specifier.replace(/\./g, "/"));
|
|
4873
5090
|
if (!normalized) {
|
|
4874
5091
|
return [];
|
|
4875
5092
|
}
|
|
4876
|
-
|
|
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);
|
|
4877
5113
|
}
|
|
4878
5114
|
function findImportCandidates(manifest, codeImport, lookup) {
|
|
4879
5115
|
const language = manifest.language ?? inferCodeLanguage(manifest.originalPath ?? manifest.storedPath, manifest.mimeType);
|
|
@@ -4901,7 +5137,7 @@ function findImportCandidates(manifest, codeImport, lookup) {
|
|
|
4901
5137
|
case "dart":
|
|
4902
5138
|
return repoRelativePath && dartSpecifierLooksLocal2(codeImport.specifier) ? repoPathMatches(lookup, ...importResolutionCandidates(repoRelativePath, codeImport.specifier, candidateExtensionsFor(language))) : aliasMatches(lookup, codeImport.specifier);
|
|
4903
5139
|
case "lua":
|
|
4904
|
-
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, "/"));
|
|
4905
5141
|
case "zig":
|
|
4906
5142
|
return repoRelativePath && (!codeImport.isExternal || codeImport.specifier.endsWith(".zig")) ? repoPathMatches(lookup, ...importResolutionCandidates(repoRelativePath, codeImport.specifier, candidateExtensionsFor(language))) : aliasMatches(lookup, codeImport.specifier);
|
|
4907
5143
|
case "php":
|
|
@@ -4920,8 +5156,15 @@ function findImportCandidates(manifest, codeImport, lookup) {
|
|
|
4920
5156
|
codeImport.specifier.replace(/\\/g, "/"),
|
|
4921
5157
|
stripCodeExtension2(codeImport.specifier.replace(/\\/g, "/"))
|
|
4922
5158
|
);
|
|
4923
|
-
case "rust":
|
|
4924
|
-
|
|
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
|
+
}
|
|
4925
5168
|
case "c":
|
|
4926
5169
|
case "cpp":
|
|
4927
5170
|
return repoRelativePath && !codeImport.isExternal ? repoPathMatches(lookup, ...importResolutionCandidates(repoRelativePath, codeImport.specifier, candidateExtensionsFor(language))) : aliasMatches(lookup, codeImport.specifier);
|
|
@@ -4930,7 +5173,7 @@ function findImportCandidates(manifest, codeImport, lookup) {
|
|
|
4930
5173
|
}
|
|
4931
5174
|
}
|
|
4932
5175
|
function importLooksLocal(manifest, codeImport, candidates) {
|
|
4933
|
-
if (candidates.length
|
|
5176
|
+
if (candidates.length === 1) {
|
|
4934
5177
|
return true;
|
|
4935
5178
|
}
|
|
4936
5179
|
const language = manifest.language ?? inferCodeLanguage(manifest.originalPath ?? manifest.storedPath, manifest.mimeType);
|
|
@@ -5661,7 +5904,7 @@ async function extractEpubChapters(input) {
|
|
|
5661
5904
|
if (!markdown) {
|
|
5662
5905
|
continue;
|
|
5663
5906
|
}
|
|
5664
|
-
const chapterTitle = firstHtmlHeading(html) ||
|
|
5907
|
+
const chapterTitle = firstHtmlHeading(html) || item.href;
|
|
5665
5908
|
const normalizedTitle = normalizeWhitespace(chapterTitle);
|
|
5666
5909
|
if (!normalizedTitle || /^table of contents$/i.test(normalizedTitle)) {
|
|
5667
5910
|
continue;
|
|
@@ -6294,6 +6537,41 @@ async function appendWatchRun(rootDir, run) {
|
|
|
6294
6537
|
await appendJsonLine(paths.jobsLogPath, run);
|
|
6295
6538
|
}
|
|
6296
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
|
+
|
|
6297
6575
|
// src/source-classification.ts
|
|
6298
6576
|
import path9 from "path";
|
|
6299
6577
|
var ALL_SOURCE_CLASSES = ["first_party", "third_party", "resource", "generated"];
|
|
@@ -6723,8 +7001,7 @@ function titleFromText(fallback, content, filePath) {
|
|
|
6723
7001
|
return rstTitle;
|
|
6724
7002
|
}
|
|
6725
7003
|
}
|
|
6726
|
-
|
|
6727
|
-
return heading || fallback;
|
|
7004
|
+
return firstMarkdownHeading(content) ?? fallback;
|
|
6728
7005
|
}
|
|
6729
7006
|
function guessMimeType(target) {
|
|
6730
7007
|
if (isRstFilePath(target)) {
|
|
@@ -6888,8 +7165,7 @@ function normalizeRstExtractedText(content) {
|
|
|
6888
7165
|
}
|
|
6889
7166
|
function titleFromRst(fallback, content) {
|
|
6890
7167
|
const normalized = normalizeRstExtractedText(content);
|
|
6891
|
-
|
|
6892
|
-
return heading || fallback;
|
|
7168
|
+
return firstMarkdownHeading(normalized) ?? fallback;
|
|
6893
7169
|
}
|
|
6894
7170
|
function extractedTextForPlainSource(filePath, sourceKind, content) {
|
|
6895
7171
|
if (sourceKind === "text" && isRstFilePath(filePath)) {
|
|
@@ -9213,9 +9489,55 @@ import { z as z7 } from "zod";
|
|
|
9213
9489
|
|
|
9214
9490
|
// src/analysis.ts
|
|
9215
9491
|
import path14 from "path";
|
|
9216
|
-
import
|
|
9217
|
-
import { fromMarkdown } from "mdast-util-from-markdown";
|
|
9492
|
+
import nlp2 from "compromise";
|
|
9218
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
|
|
9219
9541
|
var ANALYSIS_FORMAT_VERSION = 7;
|
|
9220
9542
|
var sourceAnalysisSchema = z2.object({
|
|
9221
9543
|
title: z2.string().min(1),
|
|
@@ -9234,46 +9556,6 @@ var sourceAnalysisSchema = z2.object({
|
|
|
9234
9556
|
questions: z2.array(z2.string()).max(6).default([]),
|
|
9235
9557
|
tags: z2.array(z2.string()).max(5).default([])
|
|
9236
9558
|
});
|
|
9237
|
-
var STOPWORDS = /* @__PURE__ */ new Set([
|
|
9238
|
-
"about",
|
|
9239
|
-
"after",
|
|
9240
|
-
"also",
|
|
9241
|
-
"been",
|
|
9242
|
-
"being",
|
|
9243
|
-
"between",
|
|
9244
|
-
"both",
|
|
9245
|
-
"could",
|
|
9246
|
-
"does",
|
|
9247
|
-
"each",
|
|
9248
|
-
"from",
|
|
9249
|
-
"have",
|
|
9250
|
-
"into",
|
|
9251
|
-
"just",
|
|
9252
|
-
"more",
|
|
9253
|
-
"much",
|
|
9254
|
-
"only",
|
|
9255
|
-
"other",
|
|
9256
|
-
"over",
|
|
9257
|
-
"same",
|
|
9258
|
-
"some",
|
|
9259
|
-
"such",
|
|
9260
|
-
"than",
|
|
9261
|
-
"that",
|
|
9262
|
-
"their",
|
|
9263
|
-
"there",
|
|
9264
|
-
"these",
|
|
9265
|
-
"they",
|
|
9266
|
-
"this",
|
|
9267
|
-
"very",
|
|
9268
|
-
"what",
|
|
9269
|
-
"when",
|
|
9270
|
-
"where",
|
|
9271
|
-
"which",
|
|
9272
|
-
"while",
|
|
9273
|
-
"with",
|
|
9274
|
-
"would",
|
|
9275
|
-
"your"
|
|
9276
|
-
]);
|
|
9277
9559
|
var HEURISTIC_SECTION_SOURCE_KINDS = /* @__PURE__ */ new Map([
|
|
9278
9560
|
["transcript", "Transcript"],
|
|
9279
9561
|
["chat_export", "Messages"],
|
|
@@ -9282,10 +9564,7 @@ var HEURISTIC_SECTION_SOURCE_KINDS = /* @__PURE__ */ new Map([
|
|
|
9282
9564
|
]);
|
|
9283
9565
|
function extractTopTerms(text, count) {
|
|
9284
9566
|
const frequency = /* @__PURE__ */ new Map();
|
|
9285
|
-
for (const token of text
|
|
9286
|
-
if (STOPWORDS.has(token)) {
|
|
9287
|
-
continue;
|
|
9288
|
-
}
|
|
9567
|
+
for (const token of contentTokens(text)) {
|
|
9289
9568
|
frequency.set(token, (frequency.get(token) ?? 0) + 1);
|
|
9290
9569
|
}
|
|
9291
9570
|
return [...frequency.entries()].sort((left, right) => right[1] - left[1] || left[0].localeCompare(right[0])).slice(0, count).map(([token]) => token);
|
|
@@ -9293,7 +9572,7 @@ function extractTopTerms(text, count) {
|
|
|
9293
9572
|
function extractEntities(text, count) {
|
|
9294
9573
|
const candidates = [];
|
|
9295
9574
|
try {
|
|
9296
|
-
const doc =
|
|
9575
|
+
const doc = nlp2(text);
|
|
9297
9576
|
const segments = [
|
|
9298
9577
|
doc.match("#ProperNoun+").out("array"),
|
|
9299
9578
|
doc.people().out("array"),
|
|
@@ -9311,10 +9590,6 @@ function extractEntities(text, count) {
|
|
|
9311
9590
|
}
|
|
9312
9591
|
} catch {
|
|
9313
9592
|
}
|
|
9314
|
-
if (candidates.length === 0) {
|
|
9315
|
-
const matches = text.match(/\b[A-Z][A-Za-z0-9-]+(?:\s+[A-Z][A-Za-z0-9-]+){0,2}\b/g) ?? [];
|
|
9316
|
-
candidates.push(...matches.map((value) => normalizeWhitespace(value)));
|
|
9317
|
-
}
|
|
9318
9593
|
return uniqueBy(candidates, (value) => value.toLowerCase()).slice(0, count);
|
|
9319
9594
|
}
|
|
9320
9595
|
function detectPolarity(text) {
|
|
@@ -9326,26 +9601,6 @@ function detectPolarity(text) {
|
|
|
9326
9601
|
}
|
|
9327
9602
|
return "neutral";
|
|
9328
9603
|
}
|
|
9329
|
-
function parseMarkdownNodes(text) {
|
|
9330
|
-
try {
|
|
9331
|
-
const root = fromMarkdown(text);
|
|
9332
|
-
return Array.isArray(root.children) ? root.children : [];
|
|
9333
|
-
} catch {
|
|
9334
|
-
return [];
|
|
9335
|
-
}
|
|
9336
|
-
}
|
|
9337
|
-
function markdownNodeText(node) {
|
|
9338
|
-
if (node.type === "text" || node.type === "inlineCode" || node.type === "code") {
|
|
9339
|
-
return normalizeWhitespace(node.value ?? "");
|
|
9340
|
-
}
|
|
9341
|
-
if (node.type === "image") {
|
|
9342
|
-
return normalizeWhitespace(node.alt ?? "");
|
|
9343
|
-
}
|
|
9344
|
-
if (node.type === "break" || node.type === "thematicBreak") {
|
|
9345
|
-
return " ";
|
|
9346
|
-
}
|
|
9347
|
-
return normalizeWhitespace((node.children ?? []).map((child) => markdownNodeText(child)).join(" "));
|
|
9348
|
-
}
|
|
9349
9604
|
function markdownNodesText(nodes) {
|
|
9350
9605
|
return normalizeWhitespace(nodes.map((node) => markdownNodeText(node)).join("\n"));
|
|
9351
9606
|
}
|
|
@@ -10620,7 +10875,7 @@ function filterGraphBySourceClass(graph, sourceClass) {
|
|
|
10620
10875
|
}
|
|
10621
10876
|
|
|
10622
10877
|
// src/graph-enrichment.ts
|
|
10623
|
-
var
|
|
10878
|
+
var STOPWORDS = /* @__PURE__ */ new Set([
|
|
10624
10879
|
"about",
|
|
10625
10880
|
"after",
|
|
10626
10881
|
"also",
|
|
@@ -10684,7 +10939,7 @@ function addFeature(bucket, reason, value) {
|
|
|
10684
10939
|
}
|
|
10685
10940
|
function themeTokens(value) {
|
|
10686
10941
|
return uniqueBy(
|
|
10687
|
-
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)),
|
|
10688
10943
|
(token) => token
|
|
10689
10944
|
).slice(0, 6);
|
|
10690
10945
|
}
|
|
@@ -13319,8 +13574,7 @@ function getDatabaseSync() {
|
|
|
13319
13574
|
return builtin.DatabaseSync;
|
|
13320
13575
|
}
|
|
13321
13576
|
function toFtsQuery(query) {
|
|
13322
|
-
|
|
13323
|
-
return tokens.join(" OR ");
|
|
13577
|
+
return tokenize(query).join(" OR ");
|
|
13324
13578
|
}
|
|
13325
13579
|
function normalizeKind(value) {
|
|
13326
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;
|
|
@@ -13749,7 +14003,7 @@ async function resolveImageGenerationProvider(rootDir) {
|
|
|
13749
14003
|
if (!providerConfig) {
|
|
13750
14004
|
throw new Error(`No provider configured with id "${preferredProviderId}" for task "imageProvider".`);
|
|
13751
14005
|
}
|
|
13752
|
-
const { createProvider: createProvider2 } = await import("./registry-
|
|
14006
|
+
const { createProvider: createProvider2 } = await import("./registry-W6ZFRI73.js");
|
|
13753
14007
|
return createProvider2(preferredProviderId, providerConfig, rootDir);
|
|
13754
14008
|
}
|
|
13755
14009
|
async function generateOutputArtifacts(rootDir, input) {
|
|
@@ -18016,7 +18270,7 @@ async function bootstrapDemo(rootDir, input) {
|
|
|
18016
18270
|
}
|
|
18017
18271
|
|
|
18018
18272
|
// src/mcp.ts
|
|
18019
|
-
var SERVER_VERSION = "0.6.
|
|
18273
|
+
var SERVER_VERSION = "0.6.8";
|
|
18020
18274
|
async function createMcpServer(rootDir) {
|
|
18021
18275
|
const server = new McpServer({
|
|
18022
18276
|
name: "swarmvault",
|
|
@@ -18028,10 +18282,10 @@ async function createMcpServer(rootDir) {
|
|
|
18028
18282
|
{
|
|
18029
18283
|
description: "Return the current SwarmVault workspace paths and high-level counts."
|
|
18030
18284
|
},
|
|
18031
|
-
async () => {
|
|
18285
|
+
safeHandler(async () => {
|
|
18032
18286
|
const info = await getWorkspaceInfo(rootDir);
|
|
18033
18287
|
return asToolText(info);
|
|
18034
|
-
}
|
|
18288
|
+
})
|
|
18035
18289
|
);
|
|
18036
18290
|
server.registerTool(
|
|
18037
18291
|
"search_pages",
|
|
@@ -18042,10 +18296,10 @@ async function createMcpServer(rootDir) {
|
|
|
18042
18296
|
limit: z8.number().int().min(1).max(25).optional().describe("Maximum number of results")
|
|
18043
18297
|
}
|
|
18044
18298
|
},
|
|
18045
|
-
async ({ query, limit }) => {
|
|
18299
|
+
safeHandler(async ({ query, limit }) => {
|
|
18046
18300
|
const results = await searchVault(rootDir, query, limit ?? 5);
|
|
18047
18301
|
return asToolText(results);
|
|
18048
|
-
}
|
|
18302
|
+
})
|
|
18049
18303
|
);
|
|
18050
18304
|
server.registerTool(
|
|
18051
18305
|
"read_page",
|
|
@@ -18055,13 +18309,13 @@ async function createMcpServer(rootDir) {
|
|
|
18055
18309
|
path: z8.string().min(1).describe("Path relative to wiki/, for example sources/example.md")
|
|
18056
18310
|
}
|
|
18057
18311
|
},
|
|
18058
|
-
async ({ path: relativePath }) => {
|
|
18312
|
+
safeHandler(async ({ path: relativePath }) => {
|
|
18059
18313
|
const page = await readPage(rootDir, relativePath);
|
|
18060
18314
|
if (!page) {
|
|
18061
18315
|
return asToolError(`Page not found: ${relativePath}`);
|
|
18062
18316
|
}
|
|
18063
18317
|
return asToolText(page);
|
|
18064
|
-
}
|
|
18318
|
+
})
|
|
18065
18319
|
);
|
|
18066
18320
|
server.registerTool(
|
|
18067
18321
|
"list_sources",
|
|
@@ -18071,10 +18325,10 @@ async function createMcpServer(rootDir) {
|
|
|
18071
18325
|
limit: z8.number().int().min(1).max(100).optional().describe("Maximum number of manifests to return")
|
|
18072
18326
|
}
|
|
18073
18327
|
},
|
|
18074
|
-
async ({ limit }) => {
|
|
18328
|
+
safeHandler(async ({ limit }) => {
|
|
18075
18329
|
const manifests = await listManifests(rootDir);
|
|
18076
18330
|
return asToolText(limit ? manifests.slice(0, limit) : manifests);
|
|
18077
|
-
}
|
|
18331
|
+
})
|
|
18078
18332
|
);
|
|
18079
18333
|
server.registerTool(
|
|
18080
18334
|
"query_graph",
|
|
@@ -18086,22 +18340,22 @@ async function createMcpServer(rootDir) {
|
|
|
18086
18340
|
budget: z8.number().int().min(3).max(50).optional().describe("Maximum nodes to summarize")
|
|
18087
18341
|
}
|
|
18088
18342
|
},
|
|
18089
|
-
async ({ question, traversal, budget }) => {
|
|
18343
|
+
safeHandler(async ({ question, traversal, budget }) => {
|
|
18090
18344
|
const result = await queryGraphVault(rootDir, question, {
|
|
18091
18345
|
traversal,
|
|
18092
18346
|
budget
|
|
18093
18347
|
});
|
|
18094
18348
|
return asToolText(result);
|
|
18095
|
-
}
|
|
18349
|
+
})
|
|
18096
18350
|
);
|
|
18097
18351
|
server.registerTool(
|
|
18098
18352
|
"graph_report",
|
|
18099
18353
|
{
|
|
18100
18354
|
description: "Return the machine-readable graph report and trust artifact."
|
|
18101
18355
|
},
|
|
18102
|
-
async () => {
|
|
18356
|
+
safeHandler(async () => {
|
|
18103
18357
|
return asToolText(await readGraphReport(rootDir) ?? { error: "Graph report not found. Run `swarmvault compile` first." });
|
|
18104
|
-
}
|
|
18358
|
+
})
|
|
18105
18359
|
);
|
|
18106
18360
|
server.registerTool(
|
|
18107
18361
|
"get_node",
|
|
@@ -18111,9 +18365,9 @@ async function createMcpServer(rootDir) {
|
|
|
18111
18365
|
target: z8.string().min(1).describe("Node or page label/id")
|
|
18112
18366
|
}
|
|
18113
18367
|
},
|
|
18114
|
-
async ({ target }) => {
|
|
18368
|
+
safeHandler(async ({ target }) => {
|
|
18115
18369
|
return asToolText(await explainGraphVault(rootDir, target));
|
|
18116
|
-
}
|
|
18370
|
+
})
|
|
18117
18371
|
);
|
|
18118
18372
|
server.registerTool(
|
|
18119
18373
|
"get_hyperedges",
|
|
@@ -18124,9 +18378,9 @@ async function createMcpServer(rootDir) {
|
|
|
18124
18378
|
limit: z8.number().int().min(1).max(50).optional().describe("Maximum hyperedges to return")
|
|
18125
18379
|
}
|
|
18126
18380
|
},
|
|
18127
|
-
async ({ target, limit }) => {
|
|
18381
|
+
safeHandler(async ({ target, limit }) => {
|
|
18128
18382
|
return asToolText(await listGraphHyperedges(rootDir, target, limit ?? 25));
|
|
18129
|
-
}
|
|
18383
|
+
})
|
|
18130
18384
|
);
|
|
18131
18385
|
server.registerTool(
|
|
18132
18386
|
"get_neighbors",
|
|
@@ -18136,10 +18390,10 @@ async function createMcpServer(rootDir) {
|
|
|
18136
18390
|
target: z8.string().min(1).describe("Node or page label/id")
|
|
18137
18391
|
}
|
|
18138
18392
|
},
|
|
18139
|
-
async ({ target }) => {
|
|
18393
|
+
safeHandler(async ({ target }) => {
|
|
18140
18394
|
const explanation = await explainGraphVault(rootDir, target);
|
|
18141
18395
|
return asToolText(explanation.neighbors);
|
|
18142
|
-
}
|
|
18396
|
+
})
|
|
18143
18397
|
);
|
|
18144
18398
|
server.registerTool(
|
|
18145
18399
|
"shortest_path",
|
|
@@ -18150,9 +18404,9 @@ async function createMcpServer(rootDir) {
|
|
|
18150
18404
|
to: z8.string().min(1).describe("End node/page label or id")
|
|
18151
18405
|
}
|
|
18152
18406
|
},
|
|
18153
|
-
async ({ from, to }) => {
|
|
18407
|
+
safeHandler(async ({ from, to }) => {
|
|
18154
18408
|
return asToolText(await pathGraphVault(rootDir, from, to));
|
|
18155
|
-
}
|
|
18409
|
+
})
|
|
18156
18410
|
);
|
|
18157
18411
|
server.registerTool(
|
|
18158
18412
|
"god_nodes",
|
|
@@ -18162,9 +18416,9 @@ async function createMcpServer(rootDir) {
|
|
|
18162
18416
|
limit: z8.number().int().min(1).max(25).optional().describe("Maximum nodes to return")
|
|
18163
18417
|
}
|
|
18164
18418
|
},
|
|
18165
|
-
async ({ limit }) => {
|
|
18419
|
+
safeHandler(async ({ limit }) => {
|
|
18166
18420
|
return asToolText(await listGodNodes(rootDir, limit ?? 10));
|
|
18167
|
-
}
|
|
18421
|
+
})
|
|
18168
18422
|
);
|
|
18169
18423
|
server.registerTool(
|
|
18170
18424
|
"query_vault",
|
|
@@ -18176,14 +18430,14 @@ async function createMcpServer(rootDir) {
|
|
|
18176
18430
|
format: z8.enum(["markdown", "report", "slides", "chart", "image"]).optional().describe("Output format")
|
|
18177
18431
|
}
|
|
18178
18432
|
},
|
|
18179
|
-
async ({ question, save, format }) => {
|
|
18433
|
+
safeHandler(async ({ question, save, format }) => {
|
|
18180
18434
|
const result = await queryVault(rootDir, {
|
|
18181
18435
|
question,
|
|
18182
18436
|
save: save ?? true,
|
|
18183
18437
|
format
|
|
18184
18438
|
});
|
|
18185
18439
|
return asToolText(result);
|
|
18186
|
-
}
|
|
18440
|
+
})
|
|
18187
18441
|
);
|
|
18188
18442
|
server.registerTool(
|
|
18189
18443
|
"ingest_input",
|
|
@@ -18193,10 +18447,10 @@ async function createMcpServer(rootDir) {
|
|
|
18193
18447
|
input: z8.string().min(1).describe("Local path or URL to ingest")
|
|
18194
18448
|
}
|
|
18195
18449
|
},
|
|
18196
|
-
async ({ input }) => {
|
|
18450
|
+
safeHandler(async ({ input }) => {
|
|
18197
18451
|
const result = await ingestInputDetailed(rootDir, input);
|
|
18198
18452
|
return asToolText(result);
|
|
18199
|
-
}
|
|
18453
|
+
})
|
|
18200
18454
|
);
|
|
18201
18455
|
server.registerTool(
|
|
18202
18456
|
"compile_vault",
|
|
@@ -18206,20 +18460,20 @@ async function createMcpServer(rootDir) {
|
|
|
18206
18460
|
approve: z8.boolean().optional().describe("Stage a review bundle without applying active page changes")
|
|
18207
18461
|
}
|
|
18208
18462
|
},
|
|
18209
|
-
async ({ approve }) => {
|
|
18463
|
+
safeHandler(async ({ approve }) => {
|
|
18210
18464
|
const result = await compileVault(rootDir, { approve: approve ?? false });
|
|
18211
18465
|
return asToolText(result);
|
|
18212
|
-
}
|
|
18466
|
+
})
|
|
18213
18467
|
);
|
|
18214
18468
|
server.registerTool(
|
|
18215
18469
|
"lint_vault",
|
|
18216
18470
|
{
|
|
18217
18471
|
description: "Run anti-drift and vault health checks."
|
|
18218
18472
|
},
|
|
18219
|
-
async () => {
|
|
18473
|
+
safeHandler(async () => {
|
|
18220
18474
|
const findings = await lintVault(rootDir);
|
|
18221
18475
|
return asToolText(findings);
|
|
18222
|
-
}
|
|
18476
|
+
})
|
|
18223
18477
|
);
|
|
18224
18478
|
server.registerResource(
|
|
18225
18479
|
"swarmvault-config",
|
|
@@ -18390,6 +18644,17 @@ function asToolError(message) {
|
|
|
18390
18644
|
]
|
|
18391
18645
|
};
|
|
18392
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
|
+
}
|
|
18393
18658
|
function asTextResource(uri, text) {
|
|
18394
18659
|
return {
|
|
18395
18660
|
contents: [
|
|
@@ -18637,13 +18902,22 @@ async function serveSchedules(rootDir, pollMs = 3e4) {
|
|
|
18637
18902
|
}
|
|
18638
18903
|
running = true;
|
|
18639
18904
|
try {
|
|
18640
|
-
|
|
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
|
+
}
|
|
18641
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 ?? ""));
|
|
18642
18912
|
for (const schedule of due) {
|
|
18643
18913
|
if (closed) {
|
|
18644
18914
|
break;
|
|
18645
18915
|
}
|
|
18646
|
-
|
|
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
|
+
}
|
|
18647
18921
|
}
|
|
18648
18922
|
} finally {
|
|
18649
18923
|
running = false;
|
|
@@ -20586,10 +20860,6 @@ var MAX_BACKOFF_MS = 3e4;
|
|
|
20586
20860
|
var BACKOFF_THRESHOLD = 3;
|
|
20587
20861
|
var CRITICAL_THRESHOLD = 10;
|
|
20588
20862
|
var REPO_WATCH_IGNORES = /* @__PURE__ */ new Set([".git", ".venv"]);
|
|
20589
|
-
function withinRoot3(rootPath, targetPath) {
|
|
20590
|
-
const relative = path27.relative(rootPath, targetPath);
|
|
20591
|
-
return relative === "" || !relative.startsWith("..") && !path27.isAbsolute(relative);
|
|
20592
|
-
}
|
|
20593
20863
|
function hasIgnoredRepoSegment(baseDir, targetPath) {
|
|
20594
20864
|
const relativePath = path27.relative(baseDir, targetPath);
|
|
20595
20865
|
if (!relativePath || relativePath.startsWith("..")) {
|
|
@@ -20759,11 +21029,11 @@ async function watchVault(rootDir, options = {}) {
|
|
|
20759
21029
|
interval: 100,
|
|
20760
21030
|
ignored: (targetPath) => {
|
|
20761
21031
|
const absolutePath = path27.resolve(targetPath);
|
|
20762
|
-
const primaryTarget = watchTargets.filter((watchTarget) =>
|
|
21032
|
+
const primaryTarget = watchTargets.filter((watchTarget) => isPathWithin(watchTarget, absolutePath)).sort((left, right) => right.length - left.length)[0] ?? null;
|
|
20763
21033
|
if (!primaryTarget) {
|
|
20764
21034
|
return false;
|
|
20765
21035
|
}
|
|
20766
|
-
if (primaryTarget !== inboxWatchRoot && ignoredRoots.some((ignoreRoot) =>
|
|
21036
|
+
if (primaryTarget !== inboxWatchRoot && ignoredRoots.some((ignoreRoot) => isPathWithin(ignoreRoot, absolutePath))) {
|
|
20767
21037
|
return true;
|
|
20768
21038
|
}
|
|
20769
21039
|
return hasIgnoredRepoSegment(primaryTarget, absolutePath);
|
|
@@ -20947,7 +21217,7 @@ async function watchVault(rootDir, options = {}) {
|
|
|
20947
21217
|
}
|
|
20948
21218
|
};
|
|
20949
21219
|
const reasonForPath = (targetPath) => {
|
|
20950
|
-
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;
|
|
20951
21221
|
return path27.relative(baseDir, targetPath) || ".";
|
|
20952
21222
|
};
|
|
20953
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)}`));
|