@swarmvaultai/engine 0.6.7 → 0.7.0

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/index.js CHANGED
@@ -22,13 +22,44 @@ import {
22
22
  uniqueBy,
23
23
  writeFileIfChanged,
24
24
  writeJsonFile
25
- } from "./chunk-HRRPWXRZ.js";
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, buildClaudeHookScript(), true);
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, buildGeminiHookScript(), true);
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, buildCopilotHookScript(), true);
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, buildOpenCodePlugin());
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
  }
@@ -1755,6 +1434,7 @@ import path5 from "path";
1755
1434
  var require2 = createRequire(import.meta.url);
1756
1435
  var TREE_SITTER_RUNTIME_PACKAGE = "@vscode/tree-sitter-wasm";
1757
1436
  var TREE_SITTER_EXTRA_GRAMMARS_PACKAGE = "tree-sitter-wasms";
1437
+ var SWIFT_TREE_SITTER_OPT_IN_ENV = "SWARMVAULT_ENABLE_SWIFT_TREE_SITTER";
1758
1438
  var packageRootCache = /* @__PURE__ */ new Map();
1759
1439
  var RATIONALE_MARKERS = ["NOTE:", "IMPORTANT:", "HACK:", "WHY:", "RATIONALE:"];
1760
1440
  function stripKnownCommentPrefix(line) {
@@ -1785,7 +1465,16 @@ var grammarAssetByLanguage = {
1785
1465
  cpp: { packageName: TREE_SITTER_RUNTIME_PACKAGE, relativePath: "wasm/tree-sitter-cpp.wasm" },
1786
1466
  php: { packageName: TREE_SITTER_RUNTIME_PACKAGE, relativePath: "wasm/tree-sitter-php.wasm" },
1787
1467
  ruby: { packageName: TREE_SITTER_RUNTIME_PACKAGE, relativePath: "wasm/tree-sitter-ruby.wasm" },
1788
- powershell: { packageName: TREE_SITTER_RUNTIME_PACKAGE, relativePath: "wasm/tree-sitter-powershell.wasm" }
1468
+ powershell: { packageName: TREE_SITTER_RUNTIME_PACKAGE, relativePath: "wasm/tree-sitter-powershell.wasm" },
1469
+ swift: { packageName: TREE_SITTER_EXTRA_GRAMMARS_PACKAGE, relativePath: "out/tree-sitter-swift.wasm" },
1470
+ elixir: { packageName: TREE_SITTER_EXTRA_GRAMMARS_PACKAGE, relativePath: "out/tree-sitter-elixir.wasm" },
1471
+ ocaml: { packageName: TREE_SITTER_EXTRA_GRAMMARS_PACKAGE, relativePath: "out/tree-sitter-ocaml.wasm" },
1472
+ objc: { packageName: TREE_SITTER_EXTRA_GRAMMARS_PACKAGE, relativePath: "out/tree-sitter-objc.wasm" },
1473
+ rescript: { packageName: TREE_SITTER_EXTRA_GRAMMARS_PACKAGE, relativePath: "out/tree-sitter-rescript.wasm" },
1474
+ solidity: { packageName: TREE_SITTER_EXTRA_GRAMMARS_PACKAGE, relativePath: "out/tree-sitter-solidity.wasm" },
1475
+ html: { packageName: TREE_SITTER_EXTRA_GRAMMARS_PACKAGE, relativePath: "out/tree-sitter-html.wasm" },
1476
+ css: { packageName: TREE_SITTER_EXTRA_GRAMMARS_PACKAGE, relativePath: "out/tree-sitter-css.wasm" },
1477
+ vue: { packageName: TREE_SITTER_EXTRA_GRAMMARS_PACKAGE, relativePath: "out/tree-sitter-vue.wasm" }
1789
1478
  };
1790
1479
  function resolvePackageRoot(packageName) {
1791
1480
  const cached = packageRootCache.get(packageName);
@@ -1845,7 +1534,7 @@ function normalizeSymbolReference(value) {
1845
1534
  }
1846
1535
  function stripCodeExtension(filePath) {
1847
1536
  return filePath.replace(
1848
- /\.(?:[cm]?jsx?|tsx?|mts|cts|sh|bash|zsh|py|go|rs|java|kt|kts|scala|sc|dart|lua|zig|cs|php|c|cc|cpp|cxx|h|hh|hpp|hxx)$/i,
1537
+ /\.(?:[cm]?jsx?|tsx?|mts|cts|sh|bash|zsh|py|go|rs|java|kt|kts|scala|sc|dart|lua|zig|cs|php|c|cc|cpp|cxx|h|hh|hpp|hxx|swift|exs?|mli?|mm|resi?|sol|html?|css|vue)$/i,
1849
1538
  ""
1850
1539
  );
1851
1540
  }
@@ -2133,6 +1822,72 @@ function descendantTypeNames(node) {
2133
1822
  function quotedPath(value) {
2134
1823
  return value.replace(/^['"<]+|['">]+$/g, "").trim();
2135
1824
  }
1825
+ function neutralizePreprocessorDirectives(content) {
1826
+ const lines = content.split("\n");
1827
+ const active = [];
1828
+ const isActive = () => active.every(Boolean);
1829
+ const directiveHead = (line) => {
1830
+ const trimmed = line.trimStart();
1831
+ if (trimmed[0] !== "#") {
1832
+ return void 0;
1833
+ }
1834
+ const rest = trimmed.slice(1).trimStart();
1835
+ const match = rest.match(/^([A-Za-z]+)/);
1836
+ return match?.[1]?.toLowerCase();
1837
+ };
1838
+ const out = [];
1839
+ for (const line of lines) {
1840
+ const head = directiveHead(line);
1841
+ if (head === "if" || head === "ifdef" || head === "ifndef") {
1842
+ active.push(isActive());
1843
+ out.push("");
1844
+ continue;
1845
+ }
1846
+ if (head === "elif") {
1847
+ if (active.length > 0) {
1848
+ active[active.length - 1] = false;
1849
+ }
1850
+ out.push("");
1851
+ continue;
1852
+ }
1853
+ if (head === "else") {
1854
+ if (active.length > 0) {
1855
+ active[active.length - 1] = false;
1856
+ }
1857
+ out.push("");
1858
+ continue;
1859
+ }
1860
+ if (head === "endif") {
1861
+ if (active.length > 0) {
1862
+ active.pop();
1863
+ }
1864
+ out.push("");
1865
+ continue;
1866
+ }
1867
+ if (!isActive()) {
1868
+ out.push("");
1869
+ continue;
1870
+ }
1871
+ out.push(line);
1872
+ }
1873
+ return out.join("\n");
1874
+ }
1875
+ function detectShellDialect(content) {
1876
+ const prefix = content.slice(0, 4096);
1877
+ if (/^#!\s*(?:\/usr\/bin\/env\s+)?zsh\b/m.test(prefix)) {
1878
+ return "zsh";
1879
+ }
1880
+ if (/^\s*#compdef\b/m.test(prefix)) {
1881
+ return "zsh";
1882
+ }
1883
+ if (/\$\{\([fFsq@%]/.test(prefix)) {
1884
+ return "zsh";
1885
+ }
1886
+ if (/\b(?:setopt|unsetopt|zmodload|compinit|autoload\s+-Uz)\b/.test(prefix)) {
1887
+ return "zsh";
1888
+ }
1889
+ return "bash";
1890
+ }
2136
1891
  function diagnosticsFromTree(rootNode) {
2137
1892
  if (!rootNode.hasError) {
2138
1893
  return [];
@@ -2182,104 +1937,443 @@ function treeSitterCompatibilityDiagnostic(language, error) {
2182
1937
  column: 1
2183
1938
  };
2184
1939
  }
2185
- function parsePythonImportStatement(text) {
2186
- const match = text.trim().match(/^import\s+(.+)$/);
2187
- if (!match) {
2188
- return [];
2189
- }
2190
- return match[1].split(",").map((item) => item.trim()).filter(Boolean).map((item) => {
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
- });
2200
- }
2201
- function parsePythonFromImportStatement(text) {
2202
- const match = text.trim().match(/^from\s+([.\w]+)\s+import\s+(.+)$/);
2203
- if (!match) {
2204
- return [];
2205
- }
2206
- return [
2207
- {
2208
- specifier: match[1],
2209
- importedSymbols: match[2].split(",").map((item) => item.trim()).filter(Boolean),
2210
- isExternal: !match[1].startsWith("."),
2211
- reExport: false
2212
- }
2213
- ];
2214
- }
2215
- function parseGoImport(text) {
2216
- const match = text.trim().match(/^(?:([._A-Za-z]\w*)\s+)?"([^"]+)"$/);
2217
- if (!match) {
2218
- return void 0;
2219
- }
2220
- return {
2221
- specifier: match[2],
2222
- importedSymbols: [],
2223
- namespaceImport: match[1] && ![".", "_"].includes(match[1]) ? match[1] : void 0,
2224
- isExternal: !match[2].startsWith("."),
2225
- reExport: false
2226
- };
1940
+ function swiftTreeSitterEnabled() {
1941
+ return process.env[SWIFT_TREE_SITTER_OPT_IN_ENV] === "1";
2227
1942
  }
2228
- function parseRustUse(text) {
2229
- const cleaned = text.replace(/^pub\s+/, "").replace(/^use\s+/, "").replace(/;$/, "").trim();
2230
- const aliasMatch = cleaned.match(/\s+as\s+([A-Za-z_]\w*)$/);
2231
- const withoutAlias = aliasMatch ? cleaned.slice(0, aliasMatch.index).trim() : cleaned;
2232
- const braceMatch = withoutAlias.match(/^(.*)::\{(.+)\}$/);
2233
- const importedSymbols = braceMatch ? braceMatch[2].split(",").map((item) => item.trim()).filter(Boolean) : [aliasMatch ? `${normalizeSymbolReference(withoutAlias)} as ${aliasMatch[1]}` : normalizeSymbolReference(withoutAlias)].filter(
2234
- Boolean
2235
- );
2236
- const specifier = braceMatch ? braceMatch[1].trim() : withoutAlias;
1943
+ function swiftTreeSitterDisabledDiagnostic() {
2237
1944
  return {
2238
- specifier,
2239
- importedSymbols,
2240
- isExternal: !/^(crate|self|super)::/.test(specifier),
2241
- reExport: text.trim().startsWith("pub use ")
1945
+ code: 9012,
1946
+ category: "warning",
1947
+ message: `Swift parser-backed analysis is disabled by default because the packaged tree-sitter grammar can trigger Node/V8 out-of-memory crashes during WASM compilation. Set ${SWIFT_TREE_SITTER_OPT_IN_ENV}=1 to opt in anyway.`,
1948
+ line: 1,
1949
+ column: 1
2242
1950
  };
2243
1951
  }
2244
- function parseJavaImport(text) {
2245
- const cleaned = text.replace(/^import\s+/, "").replace(/^static\s+/, "").replace(/;$/, "").trim();
2246
- const symbolName = normalizeSymbolReference(cleaned.replace(/\.\*$/, ""));
2247
- return {
2248
- specifier: cleaned.replace(/\.\*$/, ""),
2249
- importedSymbols: symbolName ? [symbolName] : [],
2250
- isExternal: true,
2251
- reExport: false
2252
- };
1952
+ function flattenPythonDottedName(node) {
1953
+ if (!node) {
1954
+ return "";
1955
+ }
1956
+ return node.namedChildren.filter((child) => child?.type === "identifier").map((child) => child.text.trim()).filter(Boolean).join(".");
2253
1957
  }
2254
- function parseKotlinImport(text) {
2255
- const cleaned = text.trim().replace(/^import\s+/, "");
2256
- if (!cleaned) {
2257
- return void 0;
1958
+ function flattenPythonRelativeImport(node) {
1959
+ if (!node) {
1960
+ return "";
2258
1961
  }
2259
- const aliasMatch = cleaned.match(/^(.+?)\s+as\s+([A-Za-z_]\w*)$/);
2260
- const specifier = (aliasMatch ? aliasMatch[1] : cleaned).trim();
2261
- if (!specifier) {
2262
- return void 0;
1962
+ const prefixNode = node.namedChildren.find((child) => child?.type === "import_prefix") ?? null;
1963
+ const prefix = prefixNode ? prefixNode.text.trim() : "";
1964
+ const moduleNode = node.namedChildren.find((child) => child?.type === "dotted_name") ?? null;
1965
+ const module = flattenPythonDottedName(moduleNode);
1966
+ return prefix + module;
1967
+ }
1968
+ function parsePythonImportStatement(node) {
1969
+ const imports = [];
1970
+ for (const child of node.namedChildren) {
1971
+ if (!child) {
1972
+ continue;
1973
+ }
1974
+ if (child.type === "dotted_name") {
1975
+ const specifier = flattenPythonDottedName(child);
1976
+ if (!specifier) {
1977
+ continue;
1978
+ }
1979
+ imports.push({
1980
+ specifier,
1981
+ importedSymbols: [],
1982
+ isExternal: !specifier.startsWith("."),
1983
+ reExport: false
1984
+ });
1985
+ } else if (child.type === "aliased_import") {
1986
+ const moduleNode = child.namedChildren.find((inner) => inner?.type === "dotted_name") ?? null;
1987
+ const aliasNode = child.namedChildren.find((inner) => inner?.type === "identifier") ?? null;
1988
+ const specifier = flattenPythonDottedName(moduleNode);
1989
+ if (!specifier) {
1990
+ continue;
1991
+ }
1992
+ imports.push({
1993
+ specifier,
1994
+ importedSymbols: [],
1995
+ namespaceImport: aliasNode?.text.trim(),
1996
+ isExternal: !specifier.startsWith("."),
1997
+ reExport: false
1998
+ });
1999
+ }
2263
2000
  }
2264
- return {
2265
- specifier,
2266
- importedSymbols: [],
2267
- namespaceImport: aliasMatch?.[2],
2268
- isExternal: !specifier.startsWith("."),
2269
- reExport: false
2270
- };
2001
+ return imports;
2271
2002
  }
2272
- function parseScalaImport(text) {
2273
- const cleaned = text.trim().replace(/^import\s+/, "");
2274
- if (!cleaned) {
2003
+ function parsePythonFromImportStatement(node) {
2004
+ const children = node.namedChildren.filter((child) => child !== null);
2005
+ if (children.length === 0) {
2275
2006
  return [];
2276
2007
  }
2277
- return cleaned.split(",").map((item) => item.trim()).filter(Boolean).map((item) => ({
2278
- specifier: item.replace(/\s*=>\s*/g, " => "),
2279
- importedSymbols: [],
2280
- isExternal: !item.startsWith("."),
2281
- reExport: false
2282
- }));
2008
+ const [moduleNode, ...rest] = children;
2009
+ if (!moduleNode) {
2010
+ return [];
2011
+ }
2012
+ let specifier;
2013
+ if (moduleNode.type === "relative_import") {
2014
+ specifier = flattenPythonRelativeImport(moduleNode);
2015
+ } else if (moduleNode.type === "dotted_name") {
2016
+ specifier = flattenPythonDottedName(moduleNode);
2017
+ } else {
2018
+ return [];
2019
+ }
2020
+ if (!specifier) {
2021
+ return [];
2022
+ }
2023
+ const symbols = [];
2024
+ let hasWildcard = false;
2025
+ for (const entry of rest) {
2026
+ if (entry.type === "wildcard_import") {
2027
+ hasWildcard = true;
2028
+ continue;
2029
+ }
2030
+ if (entry.type === "dotted_name") {
2031
+ const name = flattenPythonDottedName(entry);
2032
+ if (name) {
2033
+ symbols.push(name);
2034
+ }
2035
+ continue;
2036
+ }
2037
+ if (entry.type === "aliased_import") {
2038
+ const moduleChild = entry.namedChildren.find((inner) => inner?.type === "dotted_name") ?? null;
2039
+ const aliasChild = entry.namedChildren.find((inner) => inner?.type === "identifier") ?? null;
2040
+ const baseName = flattenPythonDottedName(moduleChild);
2041
+ const aliasName = aliasChild?.text.trim();
2042
+ if (baseName) {
2043
+ symbols.push(aliasName ? `${baseName} as ${aliasName}` : baseName);
2044
+ }
2045
+ }
2046
+ }
2047
+ if (hasWildcard) {
2048
+ symbols.push("*");
2049
+ }
2050
+ return [
2051
+ {
2052
+ specifier,
2053
+ importedSymbols: symbols,
2054
+ isExternal: !specifier.startsWith("."),
2055
+ reExport: false
2056
+ }
2057
+ ];
2058
+ }
2059
+ function parseGoImport(spec) {
2060
+ let alias;
2061
+ let dotImport = false;
2062
+ let blankImport = false;
2063
+ let specifier;
2064
+ for (const child of spec.namedChildren) {
2065
+ if (!child) {
2066
+ continue;
2067
+ }
2068
+ switch (child.type) {
2069
+ case "package_identifier":
2070
+ alias = child.text.trim();
2071
+ break;
2072
+ case "dot":
2073
+ dotImport = true;
2074
+ break;
2075
+ case "blank_identifier":
2076
+ blankImport = true;
2077
+ break;
2078
+ case "interpreted_string_literal":
2079
+ case "raw_string_literal": {
2080
+ const content = child.namedChildren.find(
2081
+ (inner) => inner?.type === "interpreted_string_literal_content" || inner?.type === "raw_string_literal_content"
2082
+ ) ?? null;
2083
+ specifier = content ? content.text : child.text.replace(/^[`"]|[`"]$/g, "");
2084
+ break;
2085
+ }
2086
+ default:
2087
+ break;
2088
+ }
2089
+ }
2090
+ if (!specifier) {
2091
+ return void 0;
2092
+ }
2093
+ return {
2094
+ specifier,
2095
+ importedSymbols: [],
2096
+ namespaceImport: !dotImport && !blankImport ? alias : void 0,
2097
+ isExternal: !specifier.startsWith("."),
2098
+ reExport: false
2099
+ };
2100
+ }
2101
+ function flattenRustPath(node) {
2102
+ if (!node) {
2103
+ return [];
2104
+ }
2105
+ if (node.type === "crate" || node.type === "self" || node.type === "super") {
2106
+ return [node.type];
2107
+ }
2108
+ if (node.type === "identifier") {
2109
+ return [node.text.trim()].filter(Boolean);
2110
+ }
2111
+ if (node.type === "scoped_identifier") {
2112
+ const segments = [];
2113
+ for (const child of node.namedChildren) {
2114
+ if (!child) {
2115
+ continue;
2116
+ }
2117
+ segments.push(...flattenRustPath(child));
2118
+ }
2119
+ return segments;
2120
+ }
2121
+ return node.namedChildren.filter((child) => child !== null).flatMap((child) => flattenRustPath(child));
2122
+ }
2123
+ function collectRustUseLeaves(node, prefix, leaves) {
2124
+ if (!node) {
2125
+ return;
2126
+ }
2127
+ switch (node.type) {
2128
+ case "scoped_identifier": {
2129
+ const segments = flattenRustPath(node);
2130
+ if (segments.length === 0) {
2131
+ return;
2132
+ }
2133
+ leaves.push({
2134
+ segments: [...prefix, ...segments],
2135
+ symbol: segments[segments.length - 1] ?? null,
2136
+ wildcard: false
2137
+ });
2138
+ return;
2139
+ }
2140
+ case "identifier":
2141
+ case "crate":
2142
+ case "self":
2143
+ case "super": {
2144
+ const combined = [...prefix];
2145
+ if (node.type === "self" && prefix.length > 0) {
2146
+ leaves.push({ segments: combined, symbol: null, wildcard: false });
2147
+ return;
2148
+ }
2149
+ combined.push(node.type === "identifier" ? node.text.trim() : node.type);
2150
+ leaves.push({
2151
+ segments: combined,
2152
+ symbol: combined[combined.length - 1] ?? null,
2153
+ wildcard: false
2154
+ });
2155
+ return;
2156
+ }
2157
+ case "scoped_use_list": {
2158
+ const pathNode = node.namedChildren[0] ?? null;
2159
+ const listNode = node.namedChildren[1] ?? null;
2160
+ const nextPrefix = [...prefix, ...flattenRustPath(pathNode)];
2161
+ collectRustUseLeaves(listNode, nextPrefix, leaves);
2162
+ return;
2163
+ }
2164
+ case "use_list": {
2165
+ for (const child of node.namedChildren) {
2166
+ collectRustUseLeaves(child, prefix, leaves);
2167
+ }
2168
+ return;
2169
+ }
2170
+ case "use_wildcard": {
2171
+ const pathNode = node.namedChildren[0] ?? null;
2172
+ const pathSegments = pathNode ? flattenRustPath(pathNode) : [];
2173
+ leaves.push({
2174
+ segments: [...prefix, ...pathSegments],
2175
+ symbol: null,
2176
+ wildcard: true
2177
+ });
2178
+ return;
2179
+ }
2180
+ case "use_as_clause": {
2181
+ const pathNode = node.childForFieldName("path") ?? node.namedChildren[0] ?? null;
2182
+ const aliasNode = node.childForFieldName("alias") ?? node.namedChildren[1] ?? null;
2183
+ const before = leaves.length;
2184
+ collectRustUseLeaves(pathNode, prefix, leaves);
2185
+ const alias = aliasNode?.text.trim();
2186
+ if (alias) {
2187
+ for (let index = before; index < leaves.length; index += 1) {
2188
+ const leaf = leaves[index];
2189
+ if (leaf) {
2190
+ leaf.alias = alias;
2191
+ }
2192
+ }
2193
+ }
2194
+ return;
2195
+ }
2196
+ default: {
2197
+ for (const child of node.namedChildren) {
2198
+ collectRustUseLeaves(child, prefix, leaves);
2199
+ }
2200
+ }
2201
+ }
2202
+ }
2203
+ function isRustPubUse(useNode) {
2204
+ for (const child of useNode.children) {
2205
+ if (!child) {
2206
+ continue;
2207
+ }
2208
+ if (child.type === "visibility_modifier") {
2209
+ return true;
2210
+ }
2211
+ if (child.type === "use") {
2212
+ return false;
2213
+ }
2214
+ }
2215
+ return false;
2216
+ }
2217
+ function parseRustUseDeclaration(useNode) {
2218
+ const inner = useNode.namedChildren.find((child) => child !== null) ?? null;
2219
+ if (!inner) {
2220
+ return [];
2221
+ }
2222
+ const leaves = [];
2223
+ collectRustUseLeaves(inner, [], leaves);
2224
+ if (leaves.length === 0) {
2225
+ return [];
2226
+ }
2227
+ const reExport = isRustPubUse(useNode);
2228
+ return leaves.map((leaf) => {
2229
+ const specifier = leaf.segments.join("::");
2230
+ const importedSymbols = leaf.wildcard ? ["*"] : leaf.alias && leaf.symbol ? [`${leaf.symbol} as ${leaf.alias}`] : leaf.symbol ? [leaf.symbol] : [];
2231
+ return {
2232
+ specifier,
2233
+ importedSymbols,
2234
+ isExternal: !/^(?:crate|self|super)(?:$|::)/.test(specifier),
2235
+ reExport
2236
+ };
2237
+ });
2238
+ }
2239
+ function flattenJavaScopedIdentifier(node) {
2240
+ if (!node) {
2241
+ return "";
2242
+ }
2243
+ if (node.type === "identifier") {
2244
+ return node.text.trim();
2245
+ }
2246
+ if (node.type === "scoped_identifier") {
2247
+ const head = node.namedChildren[0] ?? null;
2248
+ const tail = node.namedChildren[node.namedChildren.length - 1] ?? null;
2249
+ const headText = flattenJavaScopedIdentifier(head);
2250
+ const tailText = tail && tail !== head && tail.type === "identifier" ? tail.text.trim() : "";
2251
+ return headText && tailText ? `${headText}.${tailText}` : headText || tailText;
2252
+ }
2253
+ return node.text.trim();
2254
+ }
2255
+ function parseJavaImport(node) {
2256
+ const pathNode = node.namedChildren.find((child) => child?.type === "scoped_identifier") ?? null;
2257
+ const hasAsterisk = node.namedChildren.some((child) => child?.type === "asterisk");
2258
+ const pathText = flattenJavaScopedIdentifier(pathNode);
2259
+ const specifier = hasAsterisk ? `${pathText}.*` : pathText;
2260
+ const symbolName = hasAsterisk ? "" : (pathText.split(".").pop() ?? "").trim();
2261
+ return {
2262
+ specifier,
2263
+ importedSymbols: symbolName ? [symbolName] : [],
2264
+ isExternal: true,
2265
+ reExport: false
2266
+ };
2267
+ }
2268
+ function flattenKotlinIdentifier(node) {
2269
+ if (!node) {
2270
+ return "";
2271
+ }
2272
+ if (node.type === "simple_identifier") {
2273
+ return node.text.trim();
2274
+ }
2275
+ return node.namedChildren.filter((child) => child?.type === "simple_identifier").map((child) => child.text.trim()).filter(Boolean).join(".");
2276
+ }
2277
+ function parseKotlinImport(header) {
2278
+ const identifierNode = header.namedChildren.find((child) => child?.type === "identifier") ?? null;
2279
+ const specifier = flattenKotlinIdentifier(identifierNode);
2280
+ if (!specifier) {
2281
+ return void 0;
2282
+ }
2283
+ const hasWildcard = header.descendantsOfType("wildcard_import").some((child) => child !== null);
2284
+ const aliasNode = header.namedChildren.find((child) => child?.type === "import_alias") ?? null;
2285
+ const aliasName = aliasNode ? flattenKotlinIdentifier(aliasNode.namedChildren.find((child) => child?.type === "type_identifier") ?? null) || aliasNode.text.replace(/^as\s+/, "").trim() : void 0;
2286
+ return {
2287
+ specifier: hasWildcard ? `${specifier}.*` : specifier,
2288
+ importedSymbols: hasWildcard ? ["*"] : [],
2289
+ namespaceImport: aliasName || void 0,
2290
+ isExternal: true,
2291
+ reExport: false
2292
+ };
2293
+ }
2294
+ function flattenScalaStableIdentifier(node) {
2295
+ if (!node) {
2296
+ return "";
2297
+ }
2298
+ if (node.type === "identifier") {
2299
+ return node.text.trim();
2300
+ }
2301
+ if (node.type === "stable_identifier") {
2302
+ return node.namedChildren.filter((child) => child !== null).map((child) => flattenScalaStableIdentifier(child)).filter(Boolean).join(".");
2303
+ }
2304
+ return node.text.trim();
2305
+ }
2306
+ function parseScalaImport(node) {
2307
+ const pathNode = node.namedChildren.find((child) => child?.type === "stable_identifier") ?? node.namedChildren.find((child) => child?.type === "identifier") ?? null;
2308
+ const basePath = flattenScalaStableIdentifier(pathNode);
2309
+ if (!basePath) {
2310
+ return [];
2311
+ }
2312
+ const selectorsNode = node.namedChildren.find((child) => child?.type === "import_selectors") ?? null;
2313
+ const wildcardNode = node.namedChildren.find((child) => child?.type === "wildcard") ?? null;
2314
+ if (selectorsNode) {
2315
+ const results = [];
2316
+ for (const selector of selectorsNode.namedChildren) {
2317
+ if (!selector) {
2318
+ continue;
2319
+ }
2320
+ if (selector.type === "identifier") {
2321
+ const symbol2 = selector.text.trim();
2322
+ if (symbol2) {
2323
+ results.push({
2324
+ specifier: basePath,
2325
+ importedSymbols: [symbol2],
2326
+ isExternal: !basePath.startsWith("."),
2327
+ reExport: false
2328
+ });
2329
+ }
2330
+ continue;
2331
+ }
2332
+ if (selector.type === "renamed_identifier") {
2333
+ const idChildren = selector.namedChildren.filter((child) => child?.type === "identifier");
2334
+ const [original, alias] = [idChildren[0]?.text.trim(), idChildren[1]?.text.trim()];
2335
+ if (original) {
2336
+ results.push({
2337
+ specifier: basePath,
2338
+ importedSymbols: [alias ? `${original} as ${alias}` : original],
2339
+ isExternal: !basePath.startsWith("."),
2340
+ reExport: false
2341
+ });
2342
+ }
2343
+ continue;
2344
+ }
2345
+ if (selector.type === "wildcard") {
2346
+ results.push({
2347
+ specifier: basePath,
2348
+ importedSymbols: ["*"],
2349
+ isExternal: !basePath.startsWith("."),
2350
+ reExport: false
2351
+ });
2352
+ }
2353
+ }
2354
+ return results;
2355
+ }
2356
+ if (wildcardNode) {
2357
+ return [
2358
+ {
2359
+ specifier: basePath,
2360
+ importedSymbols: ["*"],
2361
+ isExternal: !basePath.startsWith("."),
2362
+ reExport: false
2363
+ }
2364
+ ];
2365
+ }
2366
+ const segments = basePath.split(".");
2367
+ const symbol = segments.pop() ?? basePath;
2368
+ const parent = segments.join(".");
2369
+ return [
2370
+ {
2371
+ specifier: parent || basePath,
2372
+ importedSymbols: [symbol],
2373
+ isExternal: !basePath.startsWith("."),
2374
+ reExport: false
2375
+ }
2376
+ ];
2283
2377
  }
2284
2378
  function bashCommandName(commandNode) {
2285
2379
  if (!commandNode) {
@@ -2401,55 +2495,165 @@ function parseZigImport(node) {
2401
2495
  reExport: false
2402
2496
  };
2403
2497
  }
2404
- function parseCSharpUsing(text) {
2405
- const aliasMatch = text.trim().match(/^using\s+([A-Za-z_]\w*)\s*=\s*([^;]+);$/);
2406
- if (aliasMatch) {
2407
- return {
2408
- specifier: aliasMatch[2].trim(),
2409
- importedSymbols: [],
2410
- namespaceImport: aliasMatch[1],
2411
- isExternal: !aliasMatch[2].trim().startsWith("."),
2412
- reExport: false
2413
- };
2498
+ function flattenCSharpQualifiedName(node) {
2499
+ if (!node) {
2500
+ return "";
2414
2501
  }
2415
- const match = text.trim().match(/^using\s+([^;]+);$/);
2416
- if (!match) {
2502
+ if (node.type === "identifier") {
2503
+ return node.text.trim();
2504
+ }
2505
+ if (node.type === "qualified_name") {
2506
+ const [head, tail] = [node.namedChildren[0] ?? null, node.namedChildren[1] ?? null];
2507
+ const headText = flattenCSharpQualifiedName(head);
2508
+ const tailText = tail?.type === "identifier" ? tail.text.trim() : flattenCSharpQualifiedName(tail);
2509
+ return headText && tailText ? `${headText}.${tailText}` : headText || tailText;
2510
+ }
2511
+ return node.text.trim();
2512
+ }
2513
+ function parseCSharpUsing(node) {
2514
+ const namedChildren = node.namedChildren.filter((child) => child !== null);
2515
+ if (namedChildren.length === 0) {
2516
+ return void 0;
2517
+ }
2518
+ let aliasName;
2519
+ let pathNode = null;
2520
+ if (namedChildren.length >= 2 && namedChildren[0]?.type === "identifier" && namedChildren[1]) {
2521
+ aliasName = namedChildren[0].text.trim();
2522
+ pathNode = namedChildren[1];
2523
+ } else {
2524
+ pathNode = namedChildren[0] ?? null;
2525
+ }
2526
+ const specifier = flattenCSharpQualifiedName(pathNode);
2527
+ if (!specifier) {
2417
2528
  return void 0;
2418
2529
  }
2419
2530
  return {
2420
- specifier: match[1].trim(),
2531
+ specifier,
2421
2532
  importedSymbols: [],
2422
- isExternal: !match[1].trim().startsWith("."),
2533
+ namespaceImport: aliasName,
2534
+ isExternal: !specifier.startsWith("."),
2423
2535
  reExport: false
2424
2536
  };
2425
2537
  }
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
- });
2538
+ function flattenPhpQualifiedName(node) {
2539
+ if (!node) {
2540
+ return "";
2541
+ }
2542
+ if (node.type === "name") {
2543
+ return node.text.trim();
2544
+ }
2545
+ if (node.type === "namespace_name") {
2546
+ return node.namedChildren.filter((child) => child?.type === "name").map((child) => child.text.trim()).filter(Boolean).join("\\");
2547
+ }
2548
+ if (node.type === "qualified_name") {
2549
+ const parts = [];
2550
+ for (const child of node.namedChildren) {
2551
+ if (!child) {
2552
+ continue;
2553
+ }
2554
+ if (child.type === "namespace_name") {
2555
+ parts.push(flattenPhpQualifiedName(child));
2556
+ } else if (child.type === "name") {
2557
+ parts.push(child.text.trim());
2558
+ }
2559
+ }
2560
+ return parts.filter(Boolean).join("\\");
2561
+ }
2562
+ return node.text.trim();
2439
2563
  }
2440
- function parseCppInclude(text) {
2441
- const match = text.trim().match(/^#include\s+([<"].+[>"])$/);
2442
- if (!match) {
2564
+ function parsePhpUseClause(clause, prefix) {
2565
+ const names = clause.namedChildren.filter((child) => child?.type === "name");
2566
+ const qualified = clause.namedChildren.find((child) => child?.type === "qualified_name") ?? null;
2567
+ let specifier;
2568
+ let aliasName;
2569
+ if (qualified) {
2570
+ specifier = flattenPhpQualifiedName(qualified);
2571
+ if (names.length >= 1 && names[0]) {
2572
+ aliasName = names[0].text.trim();
2573
+ }
2574
+ } else if (names.length >= 1 && names[0]) {
2575
+ specifier = names[0].text.trim();
2576
+ if (names.length >= 2 && names[1]) {
2577
+ aliasName = names[1].text.trim();
2578
+ }
2579
+ } else {
2580
+ return void 0;
2581
+ }
2582
+ if (prefix && specifier) {
2583
+ specifier = `${prefix}\\${specifier}`;
2584
+ }
2585
+ if (!specifier) {
2443
2586
  return void 0;
2444
2587
  }
2445
- const specifier = quotedPath(match[1]);
2446
2588
  return {
2447
2589
  specifier,
2448
2590
  importedSymbols: [],
2449
- isExternal: match[1].startsWith("<"),
2591
+ namespaceImport: aliasName,
2592
+ isExternal: true,
2450
2593
  reExport: false
2451
2594
  };
2452
2595
  }
2596
+ function parsePhpUse(node) {
2597
+ const results = [];
2598
+ const groupNode = node.namedChildren.find((child) => child?.type === "namespace_use_group") ?? null;
2599
+ if (groupNode) {
2600
+ const prefixNode = node.namedChildren.find((child) => child?.type === "namespace_name") ?? null;
2601
+ const prefix = flattenPhpQualifiedName(prefixNode);
2602
+ for (const clause of groupNode.namedChildren) {
2603
+ if (!clause || clause.type !== "namespace_use_clause") {
2604
+ continue;
2605
+ }
2606
+ const parsed = parsePhpUseClause(clause, prefix);
2607
+ if (parsed) {
2608
+ results.push(parsed);
2609
+ }
2610
+ }
2611
+ return results;
2612
+ }
2613
+ for (const child of node.namedChildren) {
2614
+ if (!child || child.type !== "namespace_use_clause") {
2615
+ continue;
2616
+ }
2617
+ const parsed = parsePhpUseClause(child, "");
2618
+ if (parsed) {
2619
+ results.push(parsed);
2620
+ }
2621
+ }
2622
+ return results;
2623
+ }
2624
+ function parseCppInclude(node) {
2625
+ for (const child of node.namedChildren) {
2626
+ if (!child) {
2627
+ continue;
2628
+ }
2629
+ if (child.type === "system_lib_string") {
2630
+ const specifier = child.text.replace(/^</, "").replace(/>$/, "").trim();
2631
+ if (!specifier) {
2632
+ return void 0;
2633
+ }
2634
+ return {
2635
+ specifier,
2636
+ importedSymbols: [],
2637
+ isExternal: true,
2638
+ reExport: false
2639
+ };
2640
+ }
2641
+ if (child.type === "string_literal") {
2642
+ const contentNode = child.namedChildren.find((inner) => inner?.type === "string_content") ?? null;
2643
+ const specifier = (contentNode?.text ?? child.text.replace(/^"|"$/g, "")).trim();
2644
+ if (!specifier) {
2645
+ return void 0;
2646
+ }
2647
+ return {
2648
+ specifier,
2649
+ importedSymbols: [],
2650
+ isExternal: false,
2651
+ reExport: false
2652
+ };
2653
+ }
2654
+ }
2655
+ return void 0;
2656
+ }
2453
2657
  function rubyStringContent(node) {
2454
2658
  if (!node) {
2455
2659
  return void 0;
@@ -2459,7 +2663,8 @@ function rubyStringContent(node) {
2459
2663
  }
2460
2664
  function normalizePowerShellDotSourceSpecifier(raw) {
2461
2665
  const unquoted = raw.replace(/^['"]+|['"]+$/g, "").trim();
2462
- return unquoted.replace(/^\$PSScriptRoot(?:[\\/]+|$)/i, "./");
2666
+ const withoutScriptRoot = unquoted.replace(/^\$PSScriptRoot(?:[\\/]+|$)/i, "./");
2667
+ return withoutScriptRoot.replace(/\\/g, "/");
2463
2668
  }
2464
2669
  function parsePowerShellImport(commandNode) {
2465
2670
  const commandName = commandNode.descendantsOfType(["command_name", "command_name_expr"]).find((item) => item !== null)?.text.trim();
@@ -2574,24 +2779,20 @@ function zigDeclarationKind(node) {
2574
2779
  }
2575
2780
  return void 0;
2576
2781
  }
2577
- function bashCodeAnalysis(manifest, rootNode, diagnostics) {
2782
+ function bashCodeAnalysis(manifest, rootNode, diagnostics, rawContent) {
2578
2783
  const imports = [];
2579
2784
  const draftSymbols = [];
2580
2785
  const exportLabels = [];
2581
- for (const child of rootNode.namedChildren) {
2582
- if (!child) {
2583
- continue;
2584
- }
2585
- if (child.type === "command") {
2586
- const parsed = parseBashImport(child);
2587
- if (parsed) {
2588
- imports.push(parsed);
2589
- }
2590
- continue;
2591
- }
2592
- if (child.type !== "function_definition") {
2593
- continue;
2786
+ const commandNodes = rootNode.descendantsOfType("command").filter((node) => node !== null);
2787
+ for (const command of commandNodes) {
2788
+ const parsed = parseBashImport(command);
2789
+ if (parsed) {
2790
+ imports.push(parsed);
2594
2791
  }
2792
+ }
2793
+ const functionNodes = rootNode.descendantsOfType("function_definition").filter((node) => node !== null);
2794
+ const functionByName = /* @__PURE__ */ new Map();
2795
+ for (const child of functionNodes) {
2595
2796
  const name = nodeText(child.childForFieldName("name") ?? child.namedChildren.at(0) ?? null).trim();
2596
2797
  if (!name) {
2597
2798
  continue;
@@ -2607,16 +2808,44 @@ function bashCodeAnalysis(manifest, rootNode, diagnostics) {
2607
2808
  bodyText: nodeText(child.childForFieldName("body") ?? findNamedChild(child, "compound_statement"))
2608
2809
  });
2609
2810
  exportLabels.push(name);
2811
+ if (!functionByName.has(name)) {
2812
+ functionByName.set(name, child);
2813
+ }
2610
2814
  }
2611
2815
  for (let index = 0; index < draftSymbols.length; index += 1) {
2612
- const functionNode = rootNode.namedChildren.find(
2613
- (child) => child?.type === "function_definition" && nodeText(child.childForFieldName("name") ?? child.namedChildren.at(0) ?? null).trim() === draftSymbols[index]?.name
2614
- );
2615
- draftSymbols[index].callNames = bashCallNamesFromBody(
2816
+ const symbol = draftSymbols[index];
2817
+ const functionNode = functionByName.get(symbol.name);
2818
+ symbol.callNames = bashCallNamesFromBody(
2616
2819
  functionNode?.childForFieldName("body") ?? findNamedChild(functionNode, "compound_statement"),
2617
- draftSymbols[index].name
2820
+ symbol.name
2618
2821
  );
2619
2822
  }
2823
+ if (draftSymbols.length === 0 && rawContent) {
2824
+ const seen = /* @__PURE__ */ new Set();
2825
+ for (const line of rawContent.split("\n")) {
2826
+ const trimmed = line.trimStart();
2827
+ let match = trimmed.match(/^function\s+([A-Za-z_][\w-]*)\s*(?:\(\))?/);
2828
+ if (!match) {
2829
+ match = trimmed.match(/^([A-Za-z_][\w-]*)\s*\(\)/);
2830
+ }
2831
+ const name = match?.[1];
2832
+ if (!name || seen.has(name)) {
2833
+ continue;
2834
+ }
2835
+ seen.add(name);
2836
+ draftSymbols.push({
2837
+ name,
2838
+ kind: "function",
2839
+ signature: singleLineSignature(trimmed),
2840
+ exported: true,
2841
+ callNames: [],
2842
+ extendsNames: [],
2843
+ implementsNames: [],
2844
+ bodyText: ""
2845
+ });
2846
+ exportLabels.push(name);
2847
+ }
2848
+ }
2620
2849
  return finalizeCodeAnalysis(manifest, "bash", imports, draftSymbols, exportLabels, diagnostics);
2621
2850
  }
2622
2851
  function dartCodeAnalysis(manifest, rootNode, diagnostics) {
@@ -2813,11 +3042,11 @@ function pythonCodeAnalysis(manifest, rootNode, diagnostics) {
2813
3042
  continue;
2814
3043
  }
2815
3044
  if (child.type === "import_statement") {
2816
- imports.push(...parsePythonImportStatement(child.text));
3045
+ imports.push(...parsePythonImportStatement(child));
2817
3046
  continue;
2818
3047
  }
2819
3048
  if (child.type === "import_from_statement") {
2820
- imports.push(...parsePythonFromImportStatement(child.text));
3049
+ imports.push(...parsePythonFromImportStatement(child));
2821
3050
  continue;
2822
3051
  }
2823
3052
  if (child.type === "class_definition") {
@@ -2872,7 +3101,7 @@ function goCodeAnalysis(manifest, rootNode, diagnostics) {
2872
3101
  }
2873
3102
  if (child.type === "import_declaration") {
2874
3103
  for (const spec of child.descendantsOfType("import_spec")) {
2875
- const parsed = spec ? parseGoImport(spec.text) : void 0;
3104
+ const parsed = spec ? parseGoImport(spec) : void 0;
2876
3105
  if (parsed) {
2877
3106
  imports.push(parsed);
2878
3107
  }
@@ -2946,7 +3175,7 @@ function rustCodeAnalysis(manifest, rootNode, diagnostics) {
2946
3175
  continue;
2947
3176
  }
2948
3177
  if (child.type === "use_declaration") {
2949
- imports.push(parseRustUse(child.text));
3178
+ imports.push(...parseRustUseDeclaration(child));
2950
3179
  continue;
2951
3180
  }
2952
3181
  if (child.type === "mod_item") {
@@ -3025,11 +3254,15 @@ function javaCodeAnalysis(manifest, rootNode, diagnostics) {
3025
3254
  continue;
3026
3255
  }
3027
3256
  if (child.type === "package_declaration") {
3028
- packageName = child.text.replace(/^package\s+/, "").replace(/;$/, "").trim();
3257
+ const pathNode = child.namedChildren.find((inner) => inner?.type === "scoped_identifier" || inner?.type === "identifier") ?? null;
3258
+ const flattened = flattenJavaScopedIdentifier(pathNode);
3259
+ if (flattened) {
3260
+ packageName = flattened;
3261
+ }
3029
3262
  continue;
3030
3263
  }
3031
3264
  if (child.type === "import_declaration") {
3032
- imports.push(parseJavaImport(child.text));
3265
+ imports.push(parseJavaImport(child));
3033
3266
  continue;
3034
3267
  }
3035
3268
  const name = extractIdentifier(child.childForFieldName("name"));
@@ -3117,7 +3350,7 @@ function kotlinCodeAnalysis(manifest, rootNode, diagnostics) {
3117
3350
  }
3118
3351
  if (child.type === "import_list") {
3119
3352
  for (const importNode of child.descendantsOfType("import_header").filter((item) => item !== null)) {
3120
- const parsed = parseKotlinImport(importNode.text);
3353
+ const parsed = parseKotlinImport(importNode);
3121
3354
  if (parsed) {
3122
3355
  imports.push(parsed);
3123
3356
  }
@@ -3207,7 +3440,7 @@ function scalaCodeAnalysis(manifest, rootNode, diagnostics) {
3207
3440
  continue;
3208
3441
  }
3209
3442
  if (child.type === "import_declaration") {
3210
- imports.push(...parseScalaImport(child.text));
3443
+ imports.push(...parseScalaImport(child));
3211
3444
  continue;
3212
3445
  }
3213
3446
  if (child.type === "function_definition") {
@@ -3388,7 +3621,7 @@ function csharpCodeAnalysis(manifest, rootNode, diagnostics) {
3388
3621
  continue;
3389
3622
  }
3390
3623
  if (child.type === "using_directive") {
3391
- const parsed = parseCSharpUsing(child.text);
3624
+ const parsed = parseCSharpUsing(child);
3392
3625
  if (parsed) {
3393
3626
  imports.push(parsed);
3394
3627
  }
@@ -3397,7 +3630,23 @@ function csharpCodeAnalysis(manifest, rootNode, diagnostics) {
3397
3630
  if (child.type === "file_scoped_namespace_declaration" || child.type === "namespace_declaration") {
3398
3631
  namespaceName = nodeText(child.childForFieldName("name")) || namespaceName;
3399
3632
  if (child.type === "namespace_declaration") {
3400
- for (const nested of child.namedChildren) {
3633
+ const nameNode = child.childForFieldName("name");
3634
+ const namespaceMembers = [];
3635
+ for (const directChild of child.namedChildren) {
3636
+ if (!directChild || directChild === nameNode) {
3637
+ continue;
3638
+ }
3639
+ if (directChild.type === "declaration_list") {
3640
+ for (const inner of directChild.namedChildren) {
3641
+ if (inner) {
3642
+ namespaceMembers.push(inner);
3643
+ }
3644
+ }
3645
+ continue;
3646
+ }
3647
+ namespaceMembers.push(directChild);
3648
+ }
3649
+ for (const nested of namespaceMembers) {
3401
3650
  if (nested && nested !== child.childForFieldName("name")) {
3402
3651
  if (["class_declaration", "interface_declaration", "enum_declaration", "struct_declaration", "record_declaration"].includes(
3403
3652
  nested.type
@@ -3480,7 +3729,7 @@ function phpCodeAnalysis(manifest, rootNode, diagnostics) {
3480
3729
  continue;
3481
3730
  }
3482
3731
  if (child.type === "namespace_use_declaration") {
3483
- imports.push(...parsePhpUse(child.text));
3732
+ imports.push(...parsePhpUse(child));
3484
3733
  continue;
3485
3734
  }
3486
3735
  const name = extractIdentifier(child.childForFieldName("name"));
@@ -3610,80 +3859,1234 @@ function rubyCodeAnalysis(manifest, rootNode, diagnostics) {
3610
3859
  exportLabels.push(symbolName);
3611
3860
  }
3612
3861
  }
3613
- };
3614
- visitStatements(rootNode, void 0, []);
3615
- return finalizeCodeAnalysis(manifest, "ruby", imports, draftSymbols, exportLabels, diagnostics, {
3616
- namespace: namespaceName
3617
- });
3862
+ };
3863
+ visitStatements(rootNode, void 0, []);
3864
+ return finalizeCodeAnalysis(manifest, "ruby", imports, draftSymbols, exportLabels, diagnostics, {
3865
+ namespace: namespaceName
3866
+ });
3867
+ }
3868
+ function powershellCodeAnalysis(manifest, rootNode, diagnostics) {
3869
+ const imports = [];
3870
+ const draftSymbols = [];
3871
+ const exportLabels = [];
3872
+ for (const child of rootNode.descendantsOfType(["command", "class_statement", "function_statement"]).filter((item) => item !== null)) {
3873
+ if (child.type === "command") {
3874
+ const parsed = parsePowerShellImport(child);
3875
+ if (parsed) {
3876
+ imports.push(parsed);
3877
+ }
3878
+ continue;
3879
+ }
3880
+ if (child.type === "class_statement") {
3881
+ const names = child.namedChildren.filter((item) => item !== null && item.type === "simple_name").map((item) => item.text.trim());
3882
+ const className = names[0];
3883
+ if (!className) {
3884
+ continue;
3885
+ }
3886
+ draftSymbols.push({
3887
+ name: className,
3888
+ kind: "class",
3889
+ signature: singleLineSignature(child.text),
3890
+ exported: true,
3891
+ callNames: [],
3892
+ extendsNames: names.slice(1, 2),
3893
+ implementsNames: [],
3894
+ bodyText: nodeText(child.childForFieldName("body")) || child.text
3895
+ });
3896
+ exportLabels.push(className);
3897
+ for (const methodNode of child.descendantsOfType("class_method_definition").filter((item) => item !== null)) {
3898
+ const methodName = methodNode.descendantsOfType("simple_name").filter((item) => item !== null).map((item) => item.text.trim())[0];
3899
+ if (!methodName) {
3900
+ continue;
3901
+ }
3902
+ const symbolName = `${className}.${methodName}`;
3903
+ draftSymbols.push({
3904
+ name: symbolName,
3905
+ kind: "function",
3906
+ signature: singleLineSignature(methodNode.text),
3907
+ exported: true,
3908
+ callNames: [],
3909
+ extendsNames: [],
3910
+ implementsNames: [],
3911
+ bodyText: nodeText(findNamedChild(methodNode, "script_block") ?? methodNode.childForFieldName("body")) || methodNode.text
3912
+ });
3913
+ exportLabels.push(symbolName);
3914
+ }
3915
+ continue;
3916
+ }
3917
+ if (child.type === "function_statement") {
3918
+ const functionName = extractIdentifier(findNamedChild(child, "function_name") ?? child.childForFieldName("name"));
3919
+ if (!functionName) {
3920
+ continue;
3921
+ }
3922
+ draftSymbols.push({
3923
+ name: functionName,
3924
+ kind: "function",
3925
+ signature: singleLineSignature(child.text),
3926
+ exported: true,
3927
+ callNames: [],
3928
+ extendsNames: [],
3929
+ implementsNames: [],
3930
+ bodyText: nodeText(findNamedChild(child, "script_block") ?? child.childForFieldName("body")) || child.text
3931
+ });
3932
+ exportLabels.push(functionName);
3933
+ }
3934
+ }
3935
+ return finalizeCodeAnalysis(manifest, "powershell", imports, draftSymbols, exportLabels, diagnostics);
3936
+ }
3937
+ function parseSwiftImport(node) {
3938
+ const identifierNode = findNamedChild(node, "identifier");
3939
+ if (!identifierNode) {
3940
+ return void 0;
3941
+ }
3942
+ const specifier = identifierNode.text.trim();
3943
+ if (!specifier) {
3944
+ return void 0;
3945
+ }
3946
+ return {
3947
+ specifier,
3948
+ importedSymbols: [],
3949
+ // Swift does not have file-local relative imports; every `import` references
3950
+ // an external module (Foundation, UIKit, a SwiftPM package product, or the
3951
+ // current target's own module). Mark them all as external so the dependency
3952
+ // aggregator groups them with other package-level graph edges.
3953
+ isExternal: true,
3954
+ reExport: false
3955
+ };
3956
+ }
3957
+ function swiftDeclarationKindFromKeyword(node) {
3958
+ for (const child of node.children) {
3959
+ if (!child) {
3960
+ continue;
3961
+ }
3962
+ if (child.type === "struct") {
3963
+ return "struct";
3964
+ }
3965
+ if (child.type === "enum") {
3966
+ return "enum";
3967
+ }
3968
+ if (child.type === "class") {
3969
+ return "class";
3970
+ }
3971
+ }
3972
+ return "class";
3973
+ }
3974
+ function swiftVisibilityKeyword(node) {
3975
+ const modifiers = findNamedChild(node, "modifiers");
3976
+ if (!modifiers) {
3977
+ return void 0;
3978
+ }
3979
+ const visibility = findNamedChild(modifiers, "visibility_modifier");
3980
+ if (!visibility) {
3981
+ return void 0;
3982
+ }
3983
+ for (const kw of visibility.children) {
3984
+ if (!kw) {
3985
+ continue;
3986
+ }
3987
+ if (kw.type === "public" || kw.type === "private" || kw.type === "fileprivate" || kw.type === "internal" || kw.type === "open") {
3988
+ return kw.type;
3989
+ }
3990
+ }
3991
+ return void 0;
3992
+ }
3993
+ function swiftExported(node) {
3994
+ const visibility = swiftVisibilityKeyword(node);
3995
+ return visibility !== "private" && visibility !== "fileprivate";
3996
+ }
3997
+ function swiftCodeAnalysis(manifest, rootNode, diagnostics) {
3998
+ const imports = [];
3999
+ const draftSymbols = [];
4000
+ const exportLabels = [];
4001
+ const recordParentTypes = (declaration) => {
4002
+ const specifiers = declaration.namedChildren.filter((item) => item?.type === "inheritance_specifier");
4003
+ if (specifiers.length === 0) {
4004
+ return [];
4005
+ }
4006
+ const ordered = [];
4007
+ for (const specifier of specifiers) {
4008
+ const primary = findNamedChild(specifier, "user_type") ?? findNamedChild(specifier, "type_identifier") ?? specifier.namedChildren.find((item) => item !== null) ?? null;
4009
+ if (!primary) {
4010
+ continue;
4011
+ }
4012
+ const name = normalizeSymbolReference(primary.text);
4013
+ if (name) {
4014
+ ordered.push(name);
4015
+ }
4016
+ }
4017
+ return uniqueBy(ordered, (item) => item);
4018
+ };
4019
+ for (const child of rootNode.namedChildren) {
4020
+ if (!child) {
4021
+ continue;
4022
+ }
4023
+ if (child.type === "import_declaration") {
4024
+ const parsed = parseSwiftImport(child);
4025
+ if (parsed) {
4026
+ imports.push(parsed);
4027
+ }
4028
+ continue;
4029
+ }
4030
+ if (child.type === "protocol_declaration") {
4031
+ const name = extractIdentifier(findNamedChild(child, "type_identifier"));
4032
+ if (!name) {
4033
+ continue;
4034
+ }
4035
+ const parents = recordParentTypes(child);
4036
+ const exported = swiftExported(child);
4037
+ draftSymbols.push({
4038
+ name,
4039
+ kind: "interface",
4040
+ signature: singleLineSignature(child.text),
4041
+ exported,
4042
+ callNames: [],
4043
+ extendsNames: parents,
4044
+ implementsNames: [],
4045
+ bodyText: nodeText(findNamedChild(child, "protocol_body")) || child.text
4046
+ });
4047
+ if (exported) {
4048
+ exportLabels.push(name);
4049
+ }
4050
+ continue;
4051
+ }
4052
+ if (child.type === "class_declaration") {
4053
+ const name = extractIdentifier(findNamedChild(child, "type_identifier"));
4054
+ if (!name) {
4055
+ continue;
4056
+ }
4057
+ const kind = swiftDeclarationKindFromKeyword(child);
4058
+ const parentTypes = recordParentTypes(child);
4059
+ const extendsNames = kind === "class" && parentTypes.length > 0 ? [parentTypes[0]] : [];
4060
+ const implementsNames = kind === "class" ? parentTypes.slice(1) : parentTypes;
4061
+ const exported = swiftExported(child);
4062
+ const body = findNamedChild(child, "class_body") ?? findNamedChild(child, "enum_class_body");
4063
+ draftSymbols.push({
4064
+ name,
4065
+ kind,
4066
+ signature: singleLineSignature(child.text),
4067
+ exported,
4068
+ callNames: [],
4069
+ extendsNames,
4070
+ implementsNames,
4071
+ bodyText: nodeText(body) || child.text
4072
+ });
4073
+ if (exported) {
4074
+ exportLabels.push(name);
4075
+ }
4076
+ continue;
4077
+ }
4078
+ if (child.type === "typealias_declaration") {
4079
+ const name = extractIdentifier(findNamedChild(child, "type_identifier"));
4080
+ if (!name) {
4081
+ continue;
4082
+ }
4083
+ const exported = swiftExported(child);
4084
+ draftSymbols.push({
4085
+ name,
4086
+ kind: "type_alias",
4087
+ signature: singleLineSignature(child.text),
4088
+ exported,
4089
+ callNames: [],
4090
+ extendsNames: [],
4091
+ implementsNames: [],
4092
+ bodyText: child.text
4093
+ });
4094
+ if (exported) {
4095
+ exportLabels.push(name);
4096
+ }
4097
+ continue;
4098
+ }
4099
+ if (child.type === "function_declaration") {
4100
+ const name = extractIdentifier(findNamedChild(child, "simple_identifier") ?? findNamedChild(child, "identifier"));
4101
+ if (!name) {
4102
+ continue;
4103
+ }
4104
+ const exported = swiftExported(child);
4105
+ draftSymbols.push({
4106
+ name,
4107
+ kind: "function",
4108
+ signature: singleLineSignature(child.text),
4109
+ exported,
4110
+ callNames: [],
4111
+ extendsNames: [],
4112
+ implementsNames: [],
4113
+ bodyText: nodeText(findNamedChild(child, "function_body")) || child.text
4114
+ });
4115
+ if (exported) {
4116
+ exportLabels.push(name);
4117
+ }
4118
+ continue;
4119
+ }
4120
+ if (child.type === "property_declaration") {
4121
+ const exported = swiftExported(child);
4122
+ const patterns = child.namedChildren.filter((item) => item?.type === "pattern");
4123
+ for (const pattern of patterns) {
4124
+ const name = extractIdentifier(findNamedChild(pattern, "simple_identifier") ?? pattern.namedChildren[0] ?? null);
4125
+ if (!name) {
4126
+ continue;
4127
+ }
4128
+ draftSymbols.push({
4129
+ name,
4130
+ kind: "variable",
4131
+ signature: singleLineSignature(child.text),
4132
+ exported,
4133
+ callNames: [],
4134
+ extendsNames: [],
4135
+ implementsNames: [],
4136
+ bodyText: child.text
4137
+ });
4138
+ if (exported) {
4139
+ exportLabels.push(name);
4140
+ }
4141
+ }
4142
+ }
4143
+ }
4144
+ return finalizeCodeAnalysis(manifest, "swift", imports, draftSymbols, exportLabels, diagnostics);
4145
+ }
4146
+ function elixirCallIdentifier(callNode) {
4147
+ return findNamedChild(callNode, "identifier")?.text.trim() || void 0;
4148
+ }
4149
+ function elixirFirstModulePath(argumentsNode) {
4150
+ if (!argumentsNode) {
4151
+ return void 0;
4152
+ }
4153
+ for (const child of argumentsNode.namedChildren) {
4154
+ if (!child) {
4155
+ continue;
4156
+ }
4157
+ if (child.type === "alias" || child.type === "identifier") {
4158
+ const text = child.text.trim();
4159
+ if (text) {
4160
+ return text;
4161
+ }
4162
+ }
4163
+ }
4164
+ return void 0;
4165
+ }
4166
+ function elixirFunctionNameFromArguments(argumentsNode) {
4167
+ if (!argumentsNode) {
4168
+ return void 0;
4169
+ }
4170
+ const first = argumentsNode.namedChildren.find((item) => item !== null);
4171
+ if (!first) {
4172
+ return void 0;
4173
+ }
4174
+ if (first.type === "call") {
4175
+ const inner = findNamedChild(first, "identifier");
4176
+ return inner?.text.trim() || void 0;
4177
+ }
4178
+ if (first.type === "identifier") {
4179
+ return first.text.trim() || void 0;
4180
+ }
4181
+ return void 0;
4182
+ }
4183
+ var ELIXIR_IMPORT_MACROS = /* @__PURE__ */ new Set(["alias", "import", "require", "use"]);
4184
+ var ELIXIR_PUBLIC_DEF_MACROS = /* @__PURE__ */ new Set(["def", "defmacro"]);
4185
+ var ELIXIR_PRIVATE_DEF_MACROS = /* @__PURE__ */ new Set(["defp", "defmacrop"]);
4186
+ function elixirCodeAnalysis(manifest, rootNode, diagnostics) {
4187
+ const imports = [];
4188
+ const draftSymbols = [];
4189
+ const exportLabels = [];
4190
+ let primaryModuleName;
4191
+ for (const topCall of rootNode.namedChildren) {
4192
+ if (!topCall || topCall.type !== "call") {
4193
+ continue;
4194
+ }
4195
+ const macroName = elixirCallIdentifier(topCall);
4196
+ if (macroName !== "defmodule" && macroName !== "defprotocol") {
4197
+ continue;
4198
+ }
4199
+ const moduleArgs = findNamedChild(topCall, "arguments");
4200
+ const moduleName = elixirFirstModulePath(moduleArgs);
4201
+ if (!moduleName) {
4202
+ continue;
4203
+ }
4204
+ const moduleKind = macroName === "defprotocol" ? "interface" : "class";
4205
+ const moduleHeaderLine = topCall.text.split("\n")[0] ?? topCall.text;
4206
+ if (primaryModuleName === void 0) {
4207
+ primaryModuleName = moduleName;
4208
+ }
4209
+ draftSymbols.push({
4210
+ name: moduleName,
4211
+ kind: moduleKind,
4212
+ signature: singleLineSignature(moduleHeaderLine),
4213
+ // Modules and protocols are always module-level public in Elixir.
4214
+ exported: true,
4215
+ callNames: [],
4216
+ extendsNames: [],
4217
+ implementsNames: [],
4218
+ bodyText: topCall.text
4219
+ });
4220
+ exportLabels.push(moduleName);
4221
+ const doBlock = findNamedChild(topCall, "do_block");
4222
+ if (!doBlock) {
4223
+ continue;
4224
+ }
4225
+ for (const innerNode of doBlock.namedChildren) {
4226
+ if (!innerNode || innerNode.type !== "call") {
4227
+ continue;
4228
+ }
4229
+ const innerMacro = elixirCallIdentifier(innerNode);
4230
+ if (!innerMacro) {
4231
+ continue;
4232
+ }
4233
+ if (ELIXIR_IMPORT_MACROS.has(innerMacro)) {
4234
+ const importArgs = findNamedChild(innerNode, "arguments");
4235
+ const modulePath = elixirFirstModulePath(importArgs);
4236
+ if (!modulePath) {
4237
+ continue;
4238
+ }
4239
+ imports.push({
4240
+ specifier: modulePath,
4241
+ importedSymbols: [],
4242
+ // Elixir imports always target a compiled BEAM module; there is no
4243
+ // notion of "file-local" relative imports the way Python or JS use them.
4244
+ // Treat every entry as external.
4245
+ isExternal: true,
4246
+ reExport: false
4247
+ });
4248
+ continue;
4249
+ }
4250
+ if (ELIXIR_PUBLIC_DEF_MACROS.has(innerMacro) || ELIXIR_PRIVATE_DEF_MACROS.has(innerMacro)) {
4251
+ const innerArgs = findNamedChild(innerNode, "arguments");
4252
+ const fnName = elixirFunctionNameFromArguments(innerArgs);
4253
+ if (!fnName) {
4254
+ continue;
4255
+ }
4256
+ const qualifiedName = `${moduleName}.${fnName}`;
4257
+ const exported = ELIXIR_PUBLIC_DEF_MACROS.has(innerMacro);
4258
+ const headerLine = innerNode.text.split("\n")[0] ?? innerNode.text;
4259
+ draftSymbols.push({
4260
+ name: qualifiedName,
4261
+ kind: "function",
4262
+ signature: singleLineSignature(headerLine),
4263
+ exported,
4264
+ callNames: [],
4265
+ extendsNames: [],
4266
+ implementsNames: [],
4267
+ bodyText: nodeText(findNamedChild(innerNode, "do_block")) || innerNode.text
4268
+ });
4269
+ if (exported) {
4270
+ exportLabels.push(qualifiedName);
4271
+ }
4272
+ }
4273
+ }
4274
+ }
4275
+ return finalizeCodeAnalysis(manifest, "elixir", imports, draftSymbols, exportLabels, diagnostics, {
4276
+ moduleName: primaryModuleName
4277
+ });
4278
+ }
4279
+ function parseOCamlOpen(node) {
4280
+ const modulePath = findNamedChild(node, "module_path");
4281
+ if (!modulePath) {
4282
+ return void 0;
4283
+ }
4284
+ const specifier = modulePath.text.trim();
4285
+ if (!specifier) {
4286
+ return void 0;
4287
+ }
4288
+ return {
4289
+ specifier,
4290
+ importedSymbols: [],
4291
+ // Every OCaml `open` references a compiled module; there is no file-local
4292
+ // "./sibling" form. Classify as external and let resolveCodeImport's single-
4293
+ // candidate short-circuit promote it to local when an alias matches.
4294
+ isExternal: true,
4295
+ reExport: false
4296
+ };
4297
+ }
4298
+ function ocamlValueBindingKind(letBinding) {
4299
+ if (!letBinding) {
4300
+ return void 0;
4301
+ }
4302
+ const hasParameter = letBinding.namedChildren.some((child) => child?.type === "parameter");
4303
+ return hasParameter ? "function" : "variable";
4304
+ }
4305
+ function ocamlTypeKind(typeBinding) {
4306
+ if (!typeBinding) {
4307
+ return "type_alias";
4308
+ }
4309
+ for (const child of typeBinding.namedChildren) {
4310
+ if (!child) {
4311
+ continue;
4312
+ }
4313
+ if (child.type === "record_declaration") {
4314
+ return "struct";
4315
+ }
4316
+ if (child.type === "variant_declaration") {
4317
+ return "enum";
4318
+ }
4319
+ }
4320
+ return "type_alias";
4321
+ }
4322
+ function ocamlCodeAnalysis(manifest, rootNode, diagnostics) {
4323
+ const imports = [];
4324
+ const draftSymbols = [];
4325
+ const exportLabels = [];
4326
+ for (const child of rootNode.namedChildren) {
4327
+ if (!child) {
4328
+ continue;
4329
+ }
4330
+ if (child.type === "open_module") {
4331
+ const parsed = parseOCamlOpen(child);
4332
+ if (parsed) {
4333
+ imports.push(parsed);
4334
+ }
4335
+ continue;
4336
+ }
4337
+ if (child.type === "module_definition") {
4338
+ const binding = findNamedChild(child, "module_binding");
4339
+ const moduleNameNode = binding ? findNamedChild(binding, "module_name") : null;
4340
+ const name = moduleNameNode?.text.trim();
4341
+ if (!name) {
4342
+ continue;
4343
+ }
4344
+ draftSymbols.push({
4345
+ name,
4346
+ kind: "class",
4347
+ signature: singleLineSignature(child.text.split("\n")[0] ?? child.text),
4348
+ // OCaml's `let`/`module` bindings are exported from the containing
4349
+ // compilation unit unless an explicit `.mli` interface hides them.
4350
+ // Treat everything defined in a `.ml` file as exported; consumers who
4351
+ // want hiding should rely on the downstream interface-file merge.
4352
+ exported: true,
4353
+ callNames: [],
4354
+ extendsNames: [],
4355
+ implementsNames: [],
4356
+ bodyText: nodeText(findNamedChild(binding, "structure")) || child.text
4357
+ });
4358
+ exportLabels.push(name);
4359
+ continue;
4360
+ }
4361
+ if (child.type === "module_type_definition") {
4362
+ const nameNode = findNamedChild(child, "module_type_name");
4363
+ const name = nameNode?.text.trim();
4364
+ if (!name) {
4365
+ continue;
4366
+ }
4367
+ draftSymbols.push({
4368
+ name,
4369
+ kind: "interface",
4370
+ signature: singleLineSignature(child.text.split("\n")[0] ?? child.text),
4371
+ exported: true,
4372
+ callNames: [],
4373
+ extendsNames: [],
4374
+ implementsNames: [],
4375
+ bodyText: nodeText(findNamedChild(child, "signature")) || child.text
4376
+ });
4377
+ exportLabels.push(name);
4378
+ continue;
4379
+ }
4380
+ if (child.type === "type_definition") {
4381
+ const binding = findNamedChild(child, "type_binding");
4382
+ const typeConstructorNode = binding ? findNamedChild(binding, "type_constructor") : null;
4383
+ const name = typeConstructorNode?.text.trim();
4384
+ if (!name) {
4385
+ continue;
4386
+ }
4387
+ const kind = ocamlTypeKind(binding);
4388
+ draftSymbols.push({
4389
+ name,
4390
+ kind,
4391
+ signature: singleLineSignature(child.text.split("\n")[0] ?? child.text),
4392
+ exported: true,
4393
+ callNames: [],
4394
+ extendsNames: [],
4395
+ implementsNames: [],
4396
+ bodyText: child.text
4397
+ });
4398
+ exportLabels.push(name);
4399
+ continue;
4400
+ }
4401
+ if (child.type === "value_definition") {
4402
+ const binding = findNamedChild(child, "let_binding");
4403
+ if (!binding) {
4404
+ continue;
4405
+ }
4406
+ const valueNameNode = findNamedChild(binding, "value_name");
4407
+ const name = valueNameNode?.text.trim();
4408
+ if (!name) {
4409
+ continue;
4410
+ }
4411
+ const kind = ocamlValueBindingKind(binding) ?? "function";
4412
+ draftSymbols.push({
4413
+ name,
4414
+ kind,
4415
+ signature: singleLineSignature(child.text.split("\n")[0] ?? child.text),
4416
+ exported: true,
4417
+ callNames: [],
4418
+ extendsNames: [],
4419
+ implementsNames: [],
4420
+ bodyText: child.text
4421
+ });
4422
+ exportLabels.push(name);
4423
+ }
4424
+ }
4425
+ return finalizeCodeAnalysis(manifest, "ocaml", imports, draftSymbols, exportLabels, diagnostics);
4426
+ }
4427
+ function objcCodeAnalysis(manifest, rootNode, diagnostics) {
4428
+ const imports = [];
4429
+ const draftSymbols = [];
4430
+ const exportLabels = [];
4431
+ const declaredClassNames = /* @__PURE__ */ new Set();
4432
+ const functionNameFromDeclarator = (node) => {
4433
+ if (!node) {
4434
+ return void 0;
4435
+ }
4436
+ const declarator = node.childForFieldName("declarator");
4437
+ if (declarator) {
4438
+ return functionNameFromDeclarator(declarator);
4439
+ }
4440
+ return extractIdentifier(node);
4441
+ };
4442
+ for (const child of rootNode.namedChildren) {
4443
+ if (!child) {
4444
+ continue;
4445
+ }
4446
+ if (child.type === "preproc_include") {
4447
+ const parsed = parseCppInclude(child);
4448
+ if (parsed) {
4449
+ imports.push(parsed);
4450
+ }
4451
+ continue;
4452
+ }
4453
+ if (child.type === "protocol_declaration") {
4454
+ const nameNode = findNamedChild(child, "identifier");
4455
+ const name = nameNode?.text.trim();
4456
+ if (!name) {
4457
+ continue;
4458
+ }
4459
+ const refList = findNamedChild(child, "protocol_reference_list");
4460
+ const parents = refList ? uniqueBy(
4461
+ refList.namedChildren.filter((item) => item?.type === "identifier").map((item) => item.text.trim()).filter(Boolean),
4462
+ (item) => item
4463
+ ) : [];
4464
+ draftSymbols.push({
4465
+ name,
4466
+ kind: "interface",
4467
+ signature: singleLineSignature(child.text.split("\n")[0] ?? child.text),
4468
+ exported: true,
4469
+ callNames: [],
4470
+ extendsNames: parents,
4471
+ implementsNames: [],
4472
+ bodyText: child.text
4473
+ });
4474
+ exportLabels.push(name);
4475
+ continue;
4476
+ }
4477
+ if (child.type === "class_interface") {
4478
+ const identifierChildren = child.namedChildren.filter((item) => item?.type === "identifier");
4479
+ const name = identifierChildren[0]?.text.trim();
4480
+ if (!name) {
4481
+ continue;
4482
+ }
4483
+ const superclass = identifierChildren[1]?.text.trim();
4484
+ const parameterized = findNamedChild(child, "parameterized_arguments");
4485
+ const protocols = parameterized ? uniqueBy(
4486
+ parameterized.namedChildren.filter((item) => item?.type === "type_name" || item?.type === "identifier").map((item) => item.text.trim()).filter(Boolean),
4487
+ (item) => item
4488
+ ) : [];
4489
+ declaredClassNames.add(name);
4490
+ draftSymbols.push({
4491
+ name,
4492
+ kind: "class",
4493
+ signature: singleLineSignature(child.text.split("\n")[0] ?? child.text),
4494
+ exported: true,
4495
+ callNames: [],
4496
+ extendsNames: superclass ? [superclass] : [],
4497
+ implementsNames: protocols,
4498
+ bodyText: child.text
4499
+ });
4500
+ exportLabels.push(name);
4501
+ continue;
4502
+ }
4503
+ if (child.type === "class_implementation") {
4504
+ const nameNode = findNamedChild(child, "identifier");
4505
+ const name = nameNode?.text.trim();
4506
+ if (!name) {
4507
+ continue;
4508
+ }
4509
+ if (declaredClassNames.has(name)) {
4510
+ continue;
4511
+ }
4512
+ declaredClassNames.add(name);
4513
+ draftSymbols.push({
4514
+ name,
4515
+ kind: "class",
4516
+ signature: singleLineSignature(child.text.split("\n")[0] ?? child.text),
4517
+ exported: true,
4518
+ callNames: [],
4519
+ extendsNames: [],
4520
+ implementsNames: [],
4521
+ bodyText: child.text
4522
+ });
4523
+ exportLabels.push(name);
4524
+ continue;
4525
+ }
4526
+ if (child.type === "function_definition") {
4527
+ const name = functionNameFromDeclarator(child.childForFieldName("declarator"));
4528
+ if (!name) {
4529
+ continue;
4530
+ }
4531
+ draftSymbols.push({
4532
+ name,
4533
+ kind: "function",
4534
+ signature: singleLineSignature(child.text.split("\n")[0] ?? child.text),
4535
+ exported: true,
4536
+ callNames: [],
4537
+ extendsNames: [],
4538
+ implementsNames: [],
4539
+ bodyText: nodeText(child.childForFieldName("body")) || child.text
4540
+ });
4541
+ exportLabels.push(name);
4542
+ }
4543
+ }
4544
+ return finalizeCodeAnalysis(manifest, "objc", imports, draftSymbols, exportLabels, diagnostics);
4545
+ }
4546
+ function rescriptCodeAnalysis(manifest, rootNode, diagnostics) {
4547
+ const imports = [];
4548
+ const draftSymbols = [];
4549
+ const exportLabels = [];
4550
+ const rescriptTypeKind = (typeBinding) => {
4551
+ if (!typeBinding) {
4552
+ return "type_alias";
4553
+ }
4554
+ for (const child of typeBinding.namedChildren) {
4555
+ if (!child) {
4556
+ continue;
4557
+ }
4558
+ if (child.type === "variant_type") {
4559
+ return "enum";
4560
+ }
4561
+ if (child.type === "record_type") {
4562
+ return "struct";
4563
+ }
4564
+ }
4565
+ return "type_alias";
4566
+ };
4567
+ const rescriptLetBindingKind = (letBinding) => {
4568
+ if (!letBinding) {
4569
+ return "variable";
4570
+ }
4571
+ for (const child of letBinding.namedChildren) {
4572
+ if (child?.type === "function") {
4573
+ return "function";
4574
+ }
4575
+ }
4576
+ return "variable";
4577
+ };
4578
+ for (const child of rootNode.namedChildren) {
4579
+ if (!child) {
4580
+ continue;
4581
+ }
4582
+ if (child.type === "open_statement") {
4583
+ const identNode = findNamedChild(child, "module_identifier");
4584
+ const specifier = identNode?.text.trim();
4585
+ if (!specifier) {
4586
+ continue;
4587
+ }
4588
+ imports.push({
4589
+ specifier,
4590
+ importedSymbols: [],
4591
+ // ReScript modules resolve through the build system's own module graph;
4592
+ // they are never file-local in the Python "./relative" sense.
4593
+ isExternal: true,
4594
+ reExport: false
4595
+ });
4596
+ continue;
4597
+ }
4598
+ if (child.type === "module_declaration") {
4599
+ const binding = findNamedChild(child, "module_binding");
4600
+ const nameNode = binding ? findNamedChild(binding, "module_identifier") : null;
4601
+ const name = nameNode?.text.trim();
4602
+ if (!name) {
4603
+ continue;
4604
+ }
4605
+ draftSymbols.push({
4606
+ name,
4607
+ kind: "class",
4608
+ signature: singleLineSignature(child.text.split("\n")[0] ?? child.text),
4609
+ exported: true,
4610
+ callNames: [],
4611
+ extendsNames: [],
4612
+ implementsNames: [],
4613
+ bodyText: nodeText(findNamedChild(binding, "block")) || child.text
4614
+ });
4615
+ exportLabels.push(name);
4616
+ continue;
4617
+ }
4618
+ if (child.type === "type_declaration") {
4619
+ const binding = findNamedChild(child, "type_binding");
4620
+ const nameNode = binding ? findNamedChild(binding, "type_identifier") : null;
4621
+ const name = nameNode?.text.trim();
4622
+ if (!name) {
4623
+ continue;
4624
+ }
4625
+ const kind = rescriptTypeKind(binding);
4626
+ draftSymbols.push({
4627
+ name,
4628
+ kind,
4629
+ signature: singleLineSignature(child.text.split("\n")[0] ?? child.text),
4630
+ exported: true,
4631
+ callNames: [],
4632
+ extendsNames: [],
4633
+ implementsNames: [],
4634
+ bodyText: child.text
4635
+ });
4636
+ exportLabels.push(name);
4637
+ continue;
4638
+ }
4639
+ if (child.type === "let_declaration") {
4640
+ const binding = findNamedChild(child, "let_binding");
4641
+ const nameNode = binding ? findNamedChild(binding, "value_identifier") : null;
4642
+ const name = nameNode?.text.trim();
4643
+ if (!name) {
4644
+ continue;
4645
+ }
4646
+ const kind = rescriptLetBindingKind(binding);
4647
+ draftSymbols.push({
4648
+ name,
4649
+ kind,
4650
+ signature: singleLineSignature(child.text.split("\n")[0] ?? child.text),
4651
+ exported: true,
4652
+ callNames: [],
4653
+ extendsNames: [],
4654
+ implementsNames: [],
4655
+ bodyText: child.text
4656
+ });
4657
+ exportLabels.push(name);
4658
+ }
4659
+ }
4660
+ return finalizeCodeAnalysis(manifest, "rescript", imports, draftSymbols, exportLabels, diagnostics);
4661
+ }
4662
+ function parseSolidityImport(node) {
4663
+ const stringNode = node.namedChildren.find((item) => item?.type === "string");
4664
+ if (!stringNode) {
4665
+ return [];
4666
+ }
4667
+ const specifier = quotedPath(stringNode.text);
4668
+ if (!specifier) {
4669
+ return [];
4670
+ }
4671
+ const importedSymbols = uniqueBy(
4672
+ node.namedChildren.filter((item) => item?.type === "identifier").map((item) => item.text.trim()).filter(Boolean),
4673
+ (item) => item
4674
+ );
4675
+ const isLocal = specifier.startsWith("./") || specifier.startsWith("../") || specifier.startsWith("/");
4676
+ return [
4677
+ {
4678
+ specifier,
4679
+ importedSymbols,
4680
+ isExternal: !isLocal,
4681
+ reExport: false
4682
+ }
4683
+ ];
4684
+ }
4685
+ function solidityCodeAnalysis(manifest, rootNode, diagnostics) {
4686
+ const imports = [];
4687
+ const draftSymbols = [];
4688
+ const exportLabels = [];
4689
+ const collectParents = (declaration) => {
4690
+ const specifiers = declaration.namedChildren.filter((item) => item?.type === "inheritance_specifier");
4691
+ const names = [];
4692
+ for (const specifier of specifiers) {
4693
+ for (const node of specifier.namedChildren) {
4694
+ if (node && (node.type === "user_defined_type" || node.type === "identifier")) {
4695
+ const text = normalizeSymbolReference(node.text);
4696
+ if (text) {
4697
+ names.push(text);
4698
+ }
4699
+ }
4700
+ }
4701
+ }
4702
+ return uniqueBy(names, (item) => item);
4703
+ };
4704
+ for (const child of rootNode.namedChildren) {
4705
+ if (!child) {
4706
+ continue;
4707
+ }
4708
+ if (child.type === "import_directive") {
4709
+ for (const parsed of parseSolidityImport(child)) {
4710
+ imports.push(parsed);
4711
+ }
4712
+ continue;
4713
+ }
4714
+ if (child.type === "interface_declaration") {
4715
+ const nameNode = findNamedChild(child, "identifier");
4716
+ const name = nameNode?.text.trim();
4717
+ if (!name) {
4718
+ continue;
4719
+ }
4720
+ const parents = collectParents(child);
4721
+ draftSymbols.push({
4722
+ name,
4723
+ kind: "interface",
4724
+ signature: singleLineSignature(child.text.split("\n")[0] ?? child.text),
4725
+ exported: true,
4726
+ callNames: [],
4727
+ extendsNames: parents,
4728
+ implementsNames: [],
4729
+ bodyText: nodeText(findNamedChild(child, "contract_body")) || child.text
4730
+ });
4731
+ exportLabels.push(name);
4732
+ continue;
4733
+ }
4734
+ if (child.type === "library_declaration" || child.type === "contract_declaration") {
4735
+ const nameNode = findNamedChild(child, "identifier");
4736
+ const name = nameNode?.text.trim();
4737
+ if (!name) {
4738
+ continue;
4739
+ }
4740
+ const parents = child.type === "contract_declaration" ? collectParents(child) : [];
4741
+ draftSymbols.push({
4742
+ name,
4743
+ kind: "class",
4744
+ signature: singleLineSignature(child.text.split("\n")[0] ?? child.text),
4745
+ exported: true,
4746
+ callNames: [],
4747
+ extendsNames: [],
4748
+ // Solidity supports multiple inheritance; list every parent contract
4749
+ // as a `implements` edge rather than arbitrarily promoting one to
4750
+ // `extends`.
4751
+ implementsNames: parents,
4752
+ bodyText: nodeText(findNamedChild(child, "contract_body")) || child.text
4753
+ });
4754
+ exportLabels.push(name);
4755
+ continue;
4756
+ }
4757
+ if (child.type === "struct_declaration") {
4758
+ const nameNode = findNamedChild(child, "identifier");
4759
+ const name = nameNode?.text.trim();
4760
+ if (!name) {
4761
+ continue;
4762
+ }
4763
+ draftSymbols.push({
4764
+ name,
4765
+ kind: "struct",
4766
+ signature: singleLineSignature(child.text.split("\n")[0] ?? child.text),
4767
+ exported: true,
4768
+ callNames: [],
4769
+ extendsNames: [],
4770
+ implementsNames: [],
4771
+ bodyText: child.text
4772
+ });
4773
+ exportLabels.push(name);
4774
+ continue;
4775
+ }
4776
+ if (child.type === "enum_declaration") {
4777
+ const nameNode = findNamedChild(child, "identifier");
4778
+ const name = nameNode?.text.trim();
4779
+ if (!name) {
4780
+ continue;
4781
+ }
4782
+ draftSymbols.push({
4783
+ name,
4784
+ kind: "enum",
4785
+ signature: singleLineSignature(child.text.split("\n")[0] ?? child.text),
4786
+ exported: true,
4787
+ callNames: [],
4788
+ extendsNames: [],
4789
+ implementsNames: [],
4790
+ bodyText: child.text
4791
+ });
4792
+ exportLabels.push(name);
4793
+ continue;
4794
+ }
4795
+ if (child.type === "function_definition") {
4796
+ const nameNode = findNamedChild(child, "identifier");
4797
+ const name = nameNode?.text.trim();
4798
+ if (!name) {
4799
+ continue;
4800
+ }
4801
+ draftSymbols.push({
4802
+ name,
4803
+ kind: "function",
4804
+ signature: singleLineSignature(child.text.split("\n")[0] ?? child.text),
4805
+ exported: true,
4806
+ callNames: [],
4807
+ extendsNames: [],
4808
+ implementsNames: [],
4809
+ bodyText: nodeText(findNamedChild(child, "function_body")) || child.text
4810
+ });
4811
+ exportLabels.push(name);
4812
+ }
4813
+ }
4814
+ return finalizeCodeAnalysis(manifest, "solidity", imports, draftSymbols, exportLabels, diagnostics);
4815
+ }
4816
+ function htmlAttributeValue(attribute) {
4817
+ const quoted = attribute.namedChildren.find((c) => c?.type === "quoted_attribute_value");
4818
+ if (quoted) {
4819
+ const inner = quoted.namedChildren.find((c) => c?.type === "attribute_value");
4820
+ if (inner) {
4821
+ return inner.text.trim();
4822
+ }
4823
+ const raw = quoted.text;
4824
+ if (raw.length >= 2 && (raw[0] === '"' || raw[0] === "'")) {
4825
+ return raw.slice(1, -1).trim();
4826
+ }
4827
+ return raw.trim();
4828
+ }
4829
+ const bare = attribute.namedChildren.find((c) => c?.type === "attribute_value");
4830
+ return bare?.text.trim();
4831
+ }
4832
+ function htmlAttributesOf(element) {
4833
+ const out = /* @__PURE__ */ new Map();
4834
+ const startTag = findNamedChild(element, "start_tag") ?? findNamedChild(element, "self_closing_tag");
4835
+ if (!startTag) {
4836
+ return out;
4837
+ }
4838
+ for (const child of startTag.namedChildren) {
4839
+ if (!child || child.type !== "attribute") {
4840
+ continue;
4841
+ }
4842
+ const nameNode = findNamedChild(child, "attribute_name");
4843
+ const name = nameNode?.text.trim().toLowerCase();
4844
+ if (!name) {
4845
+ continue;
4846
+ }
4847
+ const value = htmlAttributeValue(child);
4848
+ if (value !== void 0) {
4849
+ out.set(name, value);
4850
+ }
4851
+ }
4852
+ return out;
4853
+ }
4854
+ function htmlTagName(element) {
4855
+ const startTag = findNamedChild(element, "start_tag") ?? findNamedChild(element, "self_closing_tag") ?? null;
4856
+ if (!startTag) {
4857
+ return void 0;
4858
+ }
4859
+ return findNamedChild(startTag, "tag_name")?.text.trim().toLowerCase();
4860
+ }
4861
+ function htmlCodeAnalysis(manifest, rootNode, diagnostics) {
4862
+ const imports = [];
4863
+ const draftSymbols = [];
4864
+ const exportLabels = [];
4865
+ const seenSymbolNames = /* @__PURE__ */ new Set();
4866
+ const isLocalAssetSpecifier = (specifier) => {
4867
+ if (!specifier) {
4868
+ return false;
4869
+ }
4870
+ if (specifier.startsWith("./") || specifier.startsWith("../") || specifier.startsWith("/")) {
4871
+ return true;
4872
+ }
4873
+ if (specifier.startsWith("http://") || specifier.startsWith("https://") || specifier.startsWith("//")) {
4874
+ return false;
4875
+ }
4876
+ return !specifier.includes(":");
4877
+ };
4878
+ const elements = rootNode.descendantsOfType(["element", "script_element", "style_element"]).filter((item) => item !== null);
4879
+ for (const element of elements) {
4880
+ const attrs = htmlAttributesOf(element);
4881
+ const tagName = htmlTagName(element);
4882
+ if (tagName === "link") {
4883
+ const rel = attrs.get("rel");
4884
+ const href = attrs.get("href");
4885
+ if (rel === "stylesheet" && href) {
4886
+ imports.push({
4887
+ specifier: href,
4888
+ importedSymbols: [],
4889
+ isExternal: !isLocalAssetSpecifier(href),
4890
+ reExport: false
4891
+ });
4892
+ }
4893
+ continue;
4894
+ }
4895
+ if (element.type === "script_element") {
4896
+ const src = attrs.get("src");
4897
+ if (src) {
4898
+ imports.push({
4899
+ specifier: src,
4900
+ importedSymbols: [],
4901
+ isExternal: !isLocalAssetSpecifier(src),
4902
+ reExport: false
4903
+ });
4904
+ }
4905
+ continue;
4906
+ }
4907
+ if (tagName && tagName.includes("-")) {
4908
+ if (!seenSymbolNames.has(tagName)) {
4909
+ seenSymbolNames.add(tagName);
4910
+ draftSymbols.push({
4911
+ name: tagName,
4912
+ kind: "class",
4913
+ signature: singleLineSignature(element.text.split("\n")[0] ?? element.text),
4914
+ exported: true,
4915
+ callNames: [],
4916
+ extendsNames: [],
4917
+ implementsNames: [],
4918
+ bodyText: element.text
4919
+ });
4920
+ exportLabels.push(tagName);
4921
+ }
4922
+ }
4923
+ const id = attrs.get("id");
4924
+ if (id && !seenSymbolNames.has(id)) {
4925
+ seenSymbolNames.add(id);
4926
+ draftSymbols.push({
4927
+ name: id,
4928
+ kind: "variable",
4929
+ signature: singleLineSignature(element.text.split("\n")[0] ?? element.text),
4930
+ exported: true,
4931
+ callNames: [],
4932
+ extendsNames: [],
4933
+ implementsNames: [],
4934
+ bodyText: element.text
4935
+ });
4936
+ exportLabels.push(id);
4937
+ }
4938
+ }
4939
+ return finalizeCodeAnalysis(manifest, "html", imports, draftSymbols, exportLabels, diagnostics);
4940
+ }
4941
+ function parseCssImport(node) {
4942
+ const directString = node.namedChildren.find((c) => c?.type === "string_value");
4943
+ if (directString) {
4944
+ const specifier = quotedPath(directString.text);
4945
+ if (!specifier) {
4946
+ return void 0;
4947
+ }
4948
+ return {
4949
+ specifier,
4950
+ importedSymbols: [],
4951
+ isExternal: !(specifier.startsWith("./") || specifier.startsWith("../") || specifier.startsWith("/")),
4952
+ reExport: false
4953
+ };
4954
+ }
4955
+ const call = node.namedChildren.find((c) => c?.type === "call_expression");
4956
+ if (call) {
4957
+ const args = findNamedChild(call, "arguments");
4958
+ const stringNode = args?.namedChildren.find((c) => c?.type === "string_value");
4959
+ if (stringNode) {
4960
+ const specifier = quotedPath(stringNode.text);
4961
+ if (!specifier) {
4962
+ return void 0;
4963
+ }
4964
+ return {
4965
+ specifier,
4966
+ importedSymbols: [],
4967
+ isExternal: !(specifier.startsWith("./") || specifier.startsWith("../") || specifier.startsWith("/")),
4968
+ reExport: false
4969
+ };
4970
+ }
4971
+ }
4972
+ return void 0;
3618
4973
  }
3619
- function powershellCodeAnalysis(manifest, rootNode, diagnostics) {
4974
+ function cssCodeAnalysis(manifest, rootNode, diagnostics) {
3620
4975
  const imports = [];
3621
4976
  const draftSymbols = [];
3622
4977
  const exportLabels = [];
3623
- for (const child of rootNode.descendantsOfType(["command", "class_statement", "function_statement"]).filter((item) => item !== null)) {
3624
- if (child.type === "command") {
3625
- const parsed = parsePowerShellImport(child);
4978
+ const seenSymbols = /* @__PURE__ */ new Set();
4979
+ const addSelectorSymbol = (name, ruleText) => {
4980
+ const trimmed = name.trim();
4981
+ if (!trimmed || seenSymbols.has(trimmed)) {
4982
+ return;
4983
+ }
4984
+ seenSymbols.add(trimmed);
4985
+ draftSymbols.push({
4986
+ name: trimmed,
4987
+ kind: "class",
4988
+ signature: singleLineSignature(ruleText.split("\n")[0] ?? ruleText),
4989
+ exported: true,
4990
+ callNames: [],
4991
+ extendsNames: [],
4992
+ implementsNames: [],
4993
+ bodyText: ruleText
4994
+ });
4995
+ exportLabels.push(trimmed);
4996
+ };
4997
+ for (const child of rootNode.namedChildren) {
4998
+ if (!child) {
4999
+ continue;
5000
+ }
5001
+ if (child.type === "import_statement") {
5002
+ const parsed = parseCssImport(child);
3626
5003
  if (parsed) {
3627
5004
  imports.push(parsed);
3628
5005
  }
3629
5006
  continue;
3630
5007
  }
3631
- if (child.type === "class_statement") {
3632
- const names = child.namedChildren.filter((item) => item !== null && item.type === "simple_name").map((item) => item.text.trim());
3633
- const className = names[0];
3634
- if (!className) {
5008
+ if (child.type === "rule_set") {
5009
+ const selectors = findNamedChild(child, "selectors");
5010
+ if (!selectors) {
3635
5011
  continue;
3636
5012
  }
3637
- draftSymbols.push({
3638
- name: className,
3639
- kind: "class",
3640
- signature: singleLineSignature(child.text),
3641
- exported: true,
3642
- callNames: [],
3643
- extendsNames: names.slice(1, 2),
3644
- implementsNames: [],
3645
- bodyText: nodeText(child.childForFieldName("body")) || child.text
3646
- });
3647
- exportLabels.push(className);
3648
- for (const methodNode of child.descendantsOfType("class_method_definition").filter((item) => item !== null)) {
3649
- const methodName = methodNode.descendantsOfType("simple_name").filter((item) => item !== null).map((item) => item.text.trim())[0];
3650
- if (!methodName) {
3651
- continue;
3652
- }
3653
- const symbolName = `${className}.${methodName}`;
5013
+ const selectorText = normalizeWhitespace(selectors.text);
5014
+ addSelectorSymbol(selectorText, child.text);
5015
+ continue;
5016
+ }
5017
+ if (child.type === "keyframes_statement") {
5018
+ const nameNode = child.namedChildren.find((c) => c?.type === "keyframes_name" || c?.type === "plain_value");
5019
+ const name = nameNode?.text.trim();
5020
+ if (name) {
5021
+ addSelectorSymbol(`@keyframes ${name}`, child.text);
5022
+ }
5023
+ }
5024
+ }
5025
+ return finalizeCodeAnalysis(manifest, "css", imports, draftSymbols, exportLabels, diagnostics);
5026
+ }
5027
+ function vueCodeAnalysis(manifest, rootNode, diagnostics) {
5028
+ const imports = [];
5029
+ const draftSymbols = [];
5030
+ const exportLabels = [];
5031
+ const seenSymbols = /* @__PURE__ */ new Set();
5032
+ const repoPath = manifest.repoRelativePath ?? path5.basename(manifest.originalPath ?? manifest.storedPath);
5033
+ const basename = path5.posix.basename(stripCodeExtension(toPosix(repoPath)));
5034
+ if (basename) {
5035
+ seenSymbols.add(basename);
5036
+ draftSymbols.push({
5037
+ name: basename,
5038
+ kind: "class",
5039
+ signature: `vue component ${basename}`,
5040
+ exported: true,
5041
+ callNames: [],
5042
+ extendsNames: [],
5043
+ implementsNames: [],
5044
+ bodyText: rootNode.text
5045
+ });
5046
+ exportLabels.push(basename);
5047
+ }
5048
+ const templateElement = rootNode.namedChildren.find((c) => c?.type === "template_element");
5049
+ if (templateElement) {
5050
+ const elements = templateElement.descendantsOfType(["element"]).filter((item) => item !== null);
5051
+ for (const element of elements) {
5052
+ const tagName = htmlTagName(element);
5053
+ const attrs = htmlAttributesOf(element);
5054
+ const startTag = findNamedChild(element, "start_tag") ?? findNamedChild(element, "self_closing_tag") ?? null;
5055
+ const rawTagName = startTag ? findNamedChild(startTag, "tag_name")?.text.trim() : void 0;
5056
+ if (rawTagName && /^[A-Z]/.test(rawTagName) && !seenSymbols.has(rawTagName)) {
5057
+ seenSymbols.add(rawTagName);
3654
5058
  draftSymbols.push({
3655
- name: symbolName,
3656
- kind: "function",
3657
- signature: singleLineSignature(methodNode.text),
5059
+ name: rawTagName,
5060
+ kind: "class",
5061
+ signature: singleLineSignature(element.text.split("\n")[0] ?? element.text),
3658
5062
  exported: true,
3659
5063
  callNames: [],
3660
5064
  extendsNames: [],
3661
5065
  implementsNames: [],
3662
- bodyText: nodeText(findNamedChild(methodNode, "script_block") ?? methodNode.childForFieldName("body")) || methodNode.text
5066
+ bodyText: element.text
3663
5067
  });
3664
- exportLabels.push(symbolName);
3665
- }
3666
- continue;
3667
- }
3668
- if (child.type === "function_statement") {
3669
- const functionName = extractIdentifier(findNamedChild(child, "function_name") ?? child.childForFieldName("name"));
3670
- if (!functionName) {
3671
- continue;
5068
+ exportLabels.push(rawTagName);
5069
+ }
5070
+ if (tagName && !tagName.includes("-") && !(rawTagName && /^[A-Z]/.test(rawTagName))) {
5071
+ const id = attrs.get("id");
5072
+ if (id && !seenSymbols.has(id)) {
5073
+ seenSymbols.add(id);
5074
+ draftSymbols.push({
5075
+ name: id,
5076
+ kind: "variable",
5077
+ signature: singleLineSignature(element.text.split("\n")[0] ?? element.text),
5078
+ exported: true,
5079
+ callNames: [],
5080
+ extendsNames: [],
5081
+ implementsNames: [],
5082
+ bodyText: element.text
5083
+ });
5084
+ exportLabels.push(id);
5085
+ }
3672
5086
  }
3673
- draftSymbols.push({
3674
- name: functionName,
3675
- kind: "function",
3676
- signature: singleLineSignature(child.text),
3677
- exported: true,
3678
- callNames: [],
3679
- extendsNames: [],
3680
- implementsNames: [],
3681
- bodyText: nodeText(findNamedChild(child, "script_block") ?? child.childForFieldName("body")) || child.text
3682
- });
3683
- exportLabels.push(functionName);
3684
5087
  }
3685
5088
  }
3686
- return finalizeCodeAnalysis(manifest, "powershell", imports, draftSymbols, exportLabels, diagnostics);
5089
+ return finalizeCodeAnalysis(manifest, "vue", imports, draftSymbols, exportLabels, diagnostics);
3687
5090
  }
3688
5091
  function cFamilyCodeAnalysis(manifest, language, rootNode, diagnostics) {
3689
5092
  const imports = [];
@@ -3704,7 +5107,7 @@ function cFamilyCodeAnalysis(manifest, language, rootNode, diagnostics) {
3704
5107
  continue;
3705
5108
  }
3706
5109
  if (child.type === "preproc_include") {
3707
- const parsed = parseCppInclude(child.text);
5110
+ const parsed = parseCppInclude(child);
3708
5111
  if (parsed) {
3709
5112
  imports.push(parsed);
3710
5113
  }
@@ -3761,16 +5164,27 @@ function cFamilyCodeAnalysis(manifest, language, rootNode, diagnostics) {
3761
5164
  return finalizeCodeAnalysis(manifest, language, imports, draftSymbols, exportLabels, diagnostics);
3762
5165
  }
3763
5166
  async function analyzeTreeSitterCode(manifest, content, language) {
5167
+ if (language === "swift" && !swiftTreeSitterEnabled()) {
5168
+ return {
5169
+ code: finalizeCodeAnalysis(manifest, language, [], [], [], [swiftTreeSitterDisabledDiagnostic()]),
5170
+ rationales: []
5171
+ };
5172
+ }
5173
+ const parseInput = language === "c" || language === "cpp" || language === "csharp" ? neutralizePreprocessorDirectives(content) : content;
3764
5174
  let tree = null;
3765
5175
  try {
3766
5176
  const module = await getTreeSitterModule();
3767
5177
  await ensureTreeSitterInit(module);
3768
5178
  const parser = new module.Parser();
3769
5179
  parser.setLanguage(await loadLanguage(language));
3770
- tree = parser.parse(content);
5180
+ tree = parser.parse(parseInput);
3771
5181
  } catch (error) {
5182
+ const diagnostic = treeSitterCompatibilityDiagnostic(language, error);
5183
+ if (language === "bash" && typeof diagnostic.message === "string" && diagnostic.message.includes("resolved is not a function")) {
5184
+ diagnostic.category = "warning";
5185
+ }
3772
5186
  return {
3773
- code: finalizeCodeAnalysis(manifest, language, [], [], [], [treeSitterCompatibilityDiagnostic(language, error)]),
5187
+ code: finalizeCodeAnalysis(manifest, language, [], [], [], [diagnostic]),
3774
5188
  rationales: []
3775
5189
  };
3776
5190
  }
@@ -3796,11 +5210,14 @@ async function analyzeTreeSitterCode(manifest, content, language) {
3796
5210
  };
3797
5211
  }
3798
5212
  try {
3799
- const diagnostics = language === "lua" ? [] : diagnosticsFromTree(tree.rootNode);
5213
+ const suppressDiagnostics = language === "lua" || language === "bash" && detectShellDialect(content) === "zsh";
5214
+ const rawDiagnostics = suppressDiagnostics ? [] : diagnosticsFromTree(tree.rootNode);
5215
+ const grammarGappedLanguages = /* @__PURE__ */ new Set(["c", "cpp", "csharp", "bash"]);
5216
+ const diagnostics = grammarGappedLanguages.has(language) ? rawDiagnostics.map((d) => d.category === "error" ? { ...d, category: "warning" } : d) : rawDiagnostics;
3800
5217
  const rationales = extractTreeSitterRationales(manifest, language, tree.rootNode);
3801
5218
  switch (language) {
3802
5219
  case "bash":
3803
- return { code: bashCodeAnalysis(manifest, tree.rootNode, diagnostics), rationales };
5220
+ return { code: bashCodeAnalysis(manifest, tree.rootNode, diagnostics, content), rationales };
3804
5221
  case "python":
3805
5222
  return { code: pythonCodeAnalysis(manifest, tree.rootNode, diagnostics), rationales };
3806
5223
  case "go":
@@ -3827,6 +5244,24 @@ async function analyzeTreeSitterCode(manifest, content, language) {
3827
5244
  return { code: rubyCodeAnalysis(manifest, tree.rootNode, diagnostics), rationales };
3828
5245
  case "powershell":
3829
5246
  return { code: powershellCodeAnalysis(manifest, tree.rootNode, diagnostics), rationales };
5247
+ case "swift":
5248
+ return { code: swiftCodeAnalysis(manifest, tree.rootNode, diagnostics), rationales };
5249
+ case "elixir":
5250
+ return { code: elixirCodeAnalysis(manifest, tree.rootNode, diagnostics), rationales };
5251
+ case "ocaml":
5252
+ return { code: ocamlCodeAnalysis(manifest, tree.rootNode, diagnostics), rationales };
5253
+ case "objc":
5254
+ return { code: objcCodeAnalysis(manifest, tree.rootNode, diagnostics), rationales };
5255
+ case "rescript":
5256
+ return { code: rescriptCodeAnalysis(manifest, tree.rootNode, diagnostics), rationales };
5257
+ case "solidity":
5258
+ return { code: solidityCodeAnalysis(manifest, tree.rootNode, diagnostics), rationales };
5259
+ case "html":
5260
+ return { code: htmlCodeAnalysis(manifest, tree.rootNode, diagnostics), rationales };
5261
+ case "css":
5262
+ return { code: cssCodeAnalysis(manifest, tree.rootNode, diagnostics), rationales };
5263
+ case "vue":
5264
+ return { code: vueCodeAnalysis(manifest, tree.rootNode, diagnostics), rationales };
3830
5265
  case "c":
3831
5266
  case "cpp":
3832
5267
  return { code: cFamilyCodeAnalysis(manifest, language, tree.rootNode, diagnostics), rationales };
@@ -4520,6 +5955,33 @@ function inferCodeLanguage(filePath, mimeType = "", options = {}) {
4520
5955
  if (extension === ".ps1" || extension === ".psm1" || extension === ".psd1") {
4521
5956
  return "powershell";
4522
5957
  }
5958
+ if (extension === ".swift") {
5959
+ return "swift";
5960
+ }
5961
+ if (extension === ".ex" || extension === ".exs") {
5962
+ return "elixir";
5963
+ }
5964
+ if (extension === ".ml" || extension === ".mli") {
5965
+ return "ocaml";
5966
+ }
5967
+ if (extension === ".m" || extension === ".mm") {
5968
+ return "objc";
5969
+ }
5970
+ if (extension === ".res" || extension === ".resi") {
5971
+ return "rescript";
5972
+ }
5973
+ if (extension === ".sol") {
5974
+ return "solidity";
5975
+ }
5976
+ if (extension === ".html" || extension === ".htm") {
5977
+ return "html";
5978
+ }
5979
+ if (extension === ".css") {
5980
+ return "css";
5981
+ }
5982
+ if (extension === ".vue") {
5983
+ return "vue";
5984
+ }
4523
5985
  if (extension === ".c") {
4524
5986
  return "c";
4525
5987
  }
@@ -4638,15 +6100,36 @@ async function readNearestGoModulePath(startPath, cache) {
4638
6100
  current = parent;
4639
6101
  }
4640
6102
  }
4641
- function rustModuleAlias(repoRelativePath) {
4642
- const withoutExt = stripCodeExtension2(normalizeAlias(repoRelativePath));
6103
+ function rustModuleAliases(repoRelativePath) {
6104
+ const withoutExt = stripCodeExtension2(normalizeAlias(repoRelativePath)).replace(/\/mod$/i, "");
6105
+ if (!withoutExt) {
6106
+ return [];
6107
+ }
6108
+ const result = [];
6109
+ const push = (moduleTail) => {
6110
+ const trimmed = moduleTail.replace(/^\/+|\/+$/g, "");
6111
+ if (!trimmed || trimmed === "lib" || trimmed === "main") {
6112
+ result.push("crate");
6113
+ return;
6114
+ }
6115
+ const rootStripped = trimmed.replace(/\/(?:lib|main)$/i, "");
6116
+ if (rootStripped !== trimmed && rootStripped) {
6117
+ result.push(`crate::${rootStripped.replace(/\//g, "::")}`);
6118
+ }
6119
+ result.push(`crate::${trimmed.replace(/\//g, "::")}`);
6120
+ };
4643
6121
  const srcIdx = withoutExt.lastIndexOf("/src/");
4644
- const withinCrate = srcIdx >= 0 ? withoutExt.slice(srcIdx + "/src/".length) : withoutExt.replace(/^src\//, "");
4645
- const trimmed = withinCrate.replace(/\/mod$/i, "");
4646
- if (!trimmed || trimmed === "lib" || trimmed === "main") {
4647
- return "crate";
6122
+ if (srcIdx >= 0) {
6123
+ push(withoutExt.slice(srcIdx + "/src/".length));
6124
+ }
6125
+ if (withoutExt.startsWith("src/")) {
6126
+ push(withoutExt.slice("src/".length));
6127
+ }
6128
+ const segments = withoutExt.split("/").filter(Boolean);
6129
+ for (let start = 0; start < segments.length; start += 1) {
6130
+ push(segments.slice(start).join("/"));
4648
6131
  }
4649
- return `crate::${trimmed.replace(/\//g, "::")}`;
6132
+ return uniqueBy(result.filter(Boolean), (item) => item);
4650
6133
  }
4651
6134
  function candidateExtensionsFor(language) {
4652
6135
  switch (language) {
@@ -4687,6 +6170,24 @@ function candidateExtensionsFor(language) {
4687
6170
  return [".c", ".h"];
4688
6171
  case "cpp":
4689
6172
  return [".cc", ".cpp", ".cxx", ".h", ".hh", ".hpp", ".hxx"];
6173
+ case "swift":
6174
+ return [".swift"];
6175
+ case "elixir":
6176
+ return [".ex", ".exs"];
6177
+ case "ocaml":
6178
+ return [".ml", ".mli"];
6179
+ case "objc":
6180
+ return [".m", ".mm", ".h"];
6181
+ case "rescript":
6182
+ return [".res", ".resi"];
6183
+ case "solidity":
6184
+ return [".sol"];
6185
+ case "html":
6186
+ return [".css", ".js", ".mjs", ".cjs", ".html", ".htm"];
6187
+ case "css":
6188
+ return [".css"];
6189
+ default:
6190
+ return [];
4690
6191
  }
4691
6192
  }
4692
6193
  async function buildCodeIndex(rootDir, manifests, analyses) {
@@ -4722,7 +6223,9 @@ async function buildCodeIndex(rootDir, manifests, analyses) {
4722
6223
  break;
4723
6224
  case "rust":
4724
6225
  if (repoRelativePath) {
4725
- recordAlias(aliases, rustModuleAlias(repoRelativePath));
6226
+ for (const alias of rustModuleAliases(repoRelativePath)) {
6227
+ recordAlias(aliases, alias);
6228
+ }
4726
6229
  }
4727
6230
  break;
4728
6231
  case "go": {
@@ -4788,6 +6291,39 @@ async function buildCodeIndex(rootDir, manifests, analyses) {
4788
6291
  case "powershell":
4789
6292
  recordAlias(aliases, basename);
4790
6293
  break;
6294
+ case "elixir":
6295
+ for (const symbol of analysis.code.symbols) {
6296
+ if (symbol.kind === "class" || symbol.kind === "interface") {
6297
+ recordAlias(aliases, symbol.name);
6298
+ }
6299
+ }
6300
+ break;
6301
+ case "ocaml": {
6302
+ if (basename) {
6303
+ const capitalized = basename.charAt(0).toUpperCase() + basename.slice(1);
6304
+ recordAlias(aliases, capitalized);
6305
+ recordAlias(aliases, basename);
6306
+ }
6307
+ for (const symbol of analysis.code.symbols) {
6308
+ if (symbol.kind === "class" || symbol.kind === "interface") {
6309
+ recordAlias(aliases, symbol.name);
6310
+ }
6311
+ }
6312
+ break;
6313
+ }
6314
+ case "rescript": {
6315
+ if (basename) {
6316
+ const capitalized = basename.charAt(0).toUpperCase() + basename.slice(1);
6317
+ recordAlias(aliases, capitalized);
6318
+ recordAlias(aliases, basename);
6319
+ }
6320
+ for (const symbol of analysis.code.symbols) {
6321
+ if (symbol.kind === "class") {
6322
+ recordAlias(aliases, symbol.name);
6323
+ }
6324
+ }
6325
+ break;
6326
+ }
4791
6327
  default:
4792
6328
  break;
4793
6329
  }
@@ -4833,6 +6369,9 @@ function aliasMatches(lookup, ...aliases) {
4833
6369
  (entry) => entry.sourceId
4834
6370
  );
4835
6371
  }
6372
+ function aliasMatchesExact(lookup, alias) {
6373
+ return lookup.byAlias.get(normalizeAlias(alias)) ?? [];
6374
+ }
4836
6375
  function repoPathMatches(lookup, ...repoPaths) {
4837
6376
  return uniqueBy(
4838
6377
  repoPaths.map((repoPath) => lookup.byRepoPath.get(normalizeAlias(repoPath))).filter((entry) => Boolean(entry)),
@@ -4850,30 +6389,119 @@ function resolvePythonRelativeAliases(repoRelativePath, specifier) {
4850
6389
  }
4851
6390
  function resolveRustAliases(manifest, specifier) {
4852
6391
  const repoRelativePath = manifest.repoRelativePath ? normalizeAlias(manifest.repoRelativePath) : "";
4853
- const currentAlias = repoRelativePath ? rustModuleAlias(repoRelativePath) : void 0;
4854
- if (!specifier.startsWith("self::") && !specifier.startsWith("super::")) {
6392
+ if (!specifier.startsWith("self::") && !specifier.startsWith("super::") && specifier !== "self" && specifier !== "super") {
4855
6393
  return [specifier];
4856
6394
  }
4857
- if (!currentAlias) {
6395
+ const candidateAliases = repoRelativePath ? rustModuleAliases(repoRelativePath) : [];
6396
+ if (candidateAliases.length === 0) {
4858
6397
  return [];
4859
6398
  }
4860
- const currentParts = currentAlias.replace(/^crate(?:::)?/, "").split("::").filter(Boolean);
4861
- if (specifier.startsWith("self::")) {
4862
- return [`crate${currentParts.length ? `::${currentParts.join("::")}` : ""}::${specifier.slice("self::".length)}`];
6399
+ const tailAfter = specifier.startsWith("self::") ? specifier.slice("self::".length) : specifier.startsWith("super::") ? specifier.slice("super::".length) : "";
6400
+ const superRelative = specifier.startsWith("super");
6401
+ const expansions = [];
6402
+ for (const currentAlias of candidateAliases) {
6403
+ const currentParts = currentAlias.replace(/^crate(?:::)?/, "").split("::").filter(Boolean);
6404
+ if (superRelative) {
6405
+ if (currentParts.length > 0) {
6406
+ const parentParts = currentParts.slice(0, -1);
6407
+ const expanded2 = `crate${parentParts.length ? `::${parentParts.join("::")}` : ""}${tailAfter ? `::${tailAfter}` : ""}`.replace(/::+/g, "::").replace(/::$/, "");
6408
+ expansions.push(expanded2);
6409
+ }
6410
+ continue;
6411
+ }
6412
+ const expanded = `crate${currentParts.length ? `::${currentParts.join("::")}` : ""}${tailAfter ? `::${tailAfter}` : ""}`.replace(/::+/g, "::").replace(/::$/, "");
6413
+ expansions.push(expanded);
6414
+ }
6415
+ return uniqueBy(expansions, (item) => item);
6416
+ }
6417
+ function rustCrateRootPrefix(repoRelativePath) {
6418
+ if (!repoRelativePath) {
6419
+ return void 0;
6420
+ }
6421
+ const normalized = normalizeAlias(repoRelativePath);
6422
+ const idx = normalized.lastIndexOf("/src/");
6423
+ if (idx >= 0) {
6424
+ return normalized.slice(0, idx + "/src/".length);
4863
6425
  }
4864
- return [
4865
- `crate${currentParts.length > 1 ? `::${currentParts.slice(0, -1).join("::")}` : ""}::${specifier.slice("super::".length)}`.replace(/::+/g, "::").replace(/::$/, "")
4866
- ];
6426
+ if (normalized.startsWith("src/")) {
6427
+ return "src/";
6428
+ }
6429
+ return void 0;
6430
+ }
6431
+ function filterRustCandidatesToSameCrate(candidates, consumerPath) {
6432
+ const normalizedConsumer = consumerPath ? normalizeAlias(consumerPath) : "";
6433
+ const withoutSelf = normalizedConsumer ? candidates.filter((entry) => normalizeAlias(entry.repoRelativePath ?? "") !== normalizedConsumer) : candidates;
6434
+ if (withoutSelf.length <= 1) {
6435
+ return withoutSelf;
6436
+ }
6437
+ const cratePrefix = rustCrateRootPrefix(consumerPath);
6438
+ if (cratePrefix) {
6439
+ const sameCrate = withoutSelf.filter((entry) => normalizeAlias(entry.repoRelativePath ?? "").startsWith(cratePrefix));
6440
+ if (sameCrate.length > 0) {
6441
+ return sameCrate;
6442
+ }
6443
+ }
6444
+ if (normalizedConsumer) {
6445
+ let dir = path6.posix.dirname(normalizedConsumer);
6446
+ while (dir && dir !== "." && dir !== "/") {
6447
+ const prefix = `${dir}/`;
6448
+ const sameTree = withoutSelf.filter((entry) => normalizeAlias(entry.repoRelativePath ?? "").startsWith(prefix));
6449
+ if (sameTree.length > 0) {
6450
+ return sameTree;
6451
+ }
6452
+ const parent = path6.posix.dirname(dir);
6453
+ if (parent === dir) {
6454
+ break;
6455
+ }
6456
+ dir = parent;
6457
+ }
6458
+ }
6459
+ return withoutSelf;
6460
+ }
6461
+ function resolveRustAliasWithStripping(alias, lookup, consumerPath) {
6462
+ const segments = alias.split("::");
6463
+ while (segments.length > 0) {
6464
+ const candidate = segments.join("::");
6465
+ const matches = aliasMatchesExact(lookup, candidate);
6466
+ const filtered = matches.length > 0 ? filterRustCandidatesToSameCrate(matches, consumerPath) : [];
6467
+ if (filtered.length > 0) {
6468
+ return filtered;
6469
+ }
6470
+ if (candidate === "crate" || candidate === "self" || candidate === "super") {
6471
+ break;
6472
+ }
6473
+ segments.pop();
6474
+ }
6475
+ return [];
4867
6476
  }
4868
6477
  function luaSpecifierLooksLocal(specifier) {
4869
6478
  return /^[A-Za-z_][A-Za-z0-9_]*(?:[./][A-Za-z_][A-Za-z0-9_]*)*$/.test(specifier);
4870
6479
  }
4871
- function resolveLuaModuleCandidates(specifier) {
6480
+ function resolveLuaModuleCandidates(specifier, repoRelativePath) {
4872
6481
  const normalized = normalizeAlias(specifier.replace(/\./g, "/"));
4873
6482
  if (!normalized) {
4874
6483
  return [];
4875
6484
  }
4876
- return uniqueBy([`${normalized}.lua`, path6.posix.join(normalized, "init.lua")], (item) => item);
6485
+ const bases = /* @__PURE__ */ new Set([normalized]);
6486
+ bases.add(`src/${normalized}`);
6487
+ bases.add(`lua/${normalized}`);
6488
+ if (repoRelativePath) {
6489
+ let dir = path6.posix.dirname(repoRelativePath);
6490
+ while (dir && dir !== "." && dir !== "/") {
6491
+ bases.add(`${dir}/${normalized}`);
6492
+ const parent = path6.posix.dirname(dir);
6493
+ if (parent === dir) {
6494
+ break;
6495
+ }
6496
+ dir = parent;
6497
+ }
6498
+ }
6499
+ const candidates = [];
6500
+ for (const base of bases) {
6501
+ candidates.push(`${base}.lua`);
6502
+ candidates.push(path6.posix.join(base, "init.lua"));
6503
+ }
6504
+ return uniqueBy(candidates, (item) => item);
4877
6505
  }
4878
6506
  function findImportCandidates(manifest, codeImport, lookup) {
4879
6507
  const language = manifest.language ?? inferCodeLanguage(manifest.originalPath ?? manifest.storedPath, manifest.mimeType);
@@ -4897,11 +6525,14 @@ function findImportCandidates(manifest, codeImport, lookup) {
4897
6525
  case "kotlin":
4898
6526
  case "scala":
4899
6527
  case "csharp":
6528
+ case "elixir":
6529
+ case "ocaml":
6530
+ case "rescript":
4900
6531
  return aliasMatches(lookup, codeImport.specifier);
4901
6532
  case "dart":
4902
6533
  return repoRelativePath && dartSpecifierLooksLocal2(codeImport.specifier) ? repoPathMatches(lookup, ...importResolutionCandidates(repoRelativePath, codeImport.specifier, candidateExtensionsFor(language))) : aliasMatches(lookup, codeImport.specifier);
4903
6534
  case "lua":
4904
- return luaSpecifierLooksLocal(codeImport.specifier) ? repoPathMatches(lookup, ...resolveLuaModuleCandidates(codeImport.specifier)) : aliasMatches(lookup, codeImport.specifier, codeImport.specifier.replace(/\./g, "/"));
6535
+ return luaSpecifierLooksLocal(codeImport.specifier) ? repoPathMatches(lookup, ...resolveLuaModuleCandidates(codeImport.specifier, repoRelativePath)) : aliasMatches(lookup, codeImport.specifier, codeImport.specifier.replace(/\./g, "/"));
4905
6536
  case "zig":
4906
6537
  return repoRelativePath && (!codeImport.isExternal || codeImport.specifier.endsWith(".zig")) ? repoPathMatches(lookup, ...importResolutionCandidates(repoRelativePath, codeImport.specifier, candidateExtensionsFor(language))) : aliasMatches(lookup, codeImport.specifier);
4907
6538
  case "php":
@@ -4920,17 +6551,28 @@ function findImportCandidates(manifest, codeImport, lookup) {
4920
6551
  codeImport.specifier.replace(/\\/g, "/"),
4921
6552
  stripCodeExtension2(codeImport.specifier.replace(/\\/g, "/"))
4922
6553
  );
4923
- case "rust":
4924
- return aliasMatches(lookup, codeImport.specifier, ...resolveRustAliases(manifest, codeImport.specifier));
6554
+ case "rust": {
6555
+ for (const alias of [codeImport.specifier, ...resolveRustAliases(manifest, codeImport.specifier)]) {
6556
+ const matches = resolveRustAliasWithStripping(alias, lookup, repoRelativePath);
6557
+ if (matches.length > 0) {
6558
+ return matches;
6559
+ }
6560
+ }
6561
+ return [];
6562
+ }
4925
6563
  case "c":
4926
6564
  case "cpp":
6565
+ case "objc":
6566
+ case "solidity":
6567
+ case "html":
6568
+ case "css":
4927
6569
  return repoRelativePath && !codeImport.isExternal ? repoPathMatches(lookup, ...importResolutionCandidates(repoRelativePath, codeImport.specifier, candidateExtensionsFor(language))) : aliasMatches(lookup, codeImport.specifier);
4928
6570
  default:
4929
6571
  return [];
4930
6572
  }
4931
6573
  }
4932
6574
  function importLooksLocal(manifest, codeImport, candidates) {
4933
- if (candidates.length > 0) {
6575
+ if (candidates.length === 1) {
4934
6576
  return true;
4935
6577
  }
4936
6578
  const language = manifest.language ?? inferCodeLanguage(manifest.originalPath ?? manifest.storedPath, manifest.mimeType);
@@ -4949,8 +6591,10 @@ function importLooksLocal(manifest, codeImport, candidates) {
4949
6591
  case "powershell":
4950
6592
  case "c":
4951
6593
  case "cpp":
6594
+ case "objc":
4952
6595
  case "kotlin":
4953
6596
  case "scala":
6597
+ case "solidity":
4954
6598
  return !codeImport.isExternal;
4955
6599
  case "bash":
4956
6600
  return bashSpecifierLooksLocal2(codeImport.specifier);
@@ -5028,6 +6672,43 @@ import { strFromU8, unzipSync } from "fflate";
5028
6672
  import { JSDOM } from "jsdom";
5029
6673
  import TurndownService from "turndown";
5030
6674
  import { z } from "zod";
6675
+
6676
+ // src/markdown-ast.ts
6677
+ import { fromMarkdown } from "mdast-util-from-markdown";
6678
+ function parseMarkdownNodes(text) {
6679
+ try {
6680
+ const root = fromMarkdown(text);
6681
+ return Array.isArray(root.children) ? root.children : [];
6682
+ } catch {
6683
+ return [];
6684
+ }
6685
+ }
6686
+ function markdownNodeText(node) {
6687
+ if (node.type === "text" || node.type === "inlineCode" || node.type === "code") {
6688
+ return normalizeWhitespace(node.value ?? "");
6689
+ }
6690
+ if (node.type === "image") {
6691
+ return normalizeWhitespace(node.alt ?? "");
6692
+ }
6693
+ if (node.type === "break" || node.type === "thematicBreak") {
6694
+ return " ";
6695
+ }
6696
+ return normalizeWhitespace((node.children ?? []).map((child) => markdownNodeText(child)).join(" "));
6697
+ }
6698
+ function firstMarkdownHeading(text) {
6699
+ const nodes = parseMarkdownNodes(text);
6700
+ for (const node of nodes) {
6701
+ if (node.type === "heading") {
6702
+ const title = markdownNodeText(node).trim();
6703
+ if (title) {
6704
+ return title;
6705
+ }
6706
+ }
6707
+ }
6708
+ return void 0;
6709
+ }
6710
+
6711
+ // src/extraction.ts
5031
6712
  var imageVisionExtractionSchema = z.object({
5032
6713
  title: z.string().min(1).nullable().optional(),
5033
6714
  summary: z.string().min(1),
@@ -5427,13 +7108,153 @@ async function extractDocxText(input) {
5427
7108
  }
5428
7109
  return {
5429
7110
  extractedText: extractedText || void 0,
5430
- artifact
7111
+ artifact
7112
+ };
7113
+ } catch (error) {
7114
+ return {
7115
+ artifact: {
7116
+ ...extractionMetadata("docx", input.mimeType, "docx_text"),
7117
+ warnings: [`DOCX text extraction failed: ${error instanceof Error ? truncate(error.message, 240) : "unknown error"}`]
7118
+ }
7119
+ };
7120
+ }
7121
+ }
7122
+ function jupyterCellSource(cell) {
7123
+ const source = cell.source;
7124
+ if (Array.isArray(source)) {
7125
+ return source.join("");
7126
+ }
7127
+ if (typeof source === "string") {
7128
+ return source;
7129
+ }
7130
+ return "";
7131
+ }
7132
+ function jupyterOutputSummary(outputs) {
7133
+ if (!Array.isArray(outputs) || outputs.length === 0) {
7134
+ return null;
7135
+ }
7136
+ const parts = [];
7137
+ for (const output of outputs) {
7138
+ const data = output.data;
7139
+ if (data && typeof data === "object") {
7140
+ const text = data["text/plain"] ?? data["text/markdown"];
7141
+ if (typeof text === "string") {
7142
+ parts.push(text.trim());
7143
+ continue;
7144
+ }
7145
+ if (Array.isArray(text)) {
7146
+ parts.push(text.join("").trim());
7147
+ continue;
7148
+ }
7149
+ }
7150
+ const textField = output.text;
7151
+ if (typeof textField === "string") {
7152
+ parts.push(textField.trim());
7153
+ continue;
7154
+ }
7155
+ if (Array.isArray(textField)) {
7156
+ parts.push(textField.join("").trim());
7157
+ }
7158
+ }
7159
+ const joined = parts.filter(Boolean).join("\n").trim();
7160
+ if (!joined) {
7161
+ return `[${outputs.length} non-text output${outputs.length === 1 ? "" : "s"}]`;
7162
+ }
7163
+ return joined.length > 1200 ? `${joined.slice(0, 1200)}
7164
+ [output truncated]` : joined;
7165
+ }
7166
+ async function extractJupyterNotebook(input) {
7167
+ try {
7168
+ const text = decodeTextBytes(input.bytes);
7169
+ const notebook = JSON.parse(text);
7170
+ const cells = Array.isArray(notebook.cells) ? notebook.cells : [];
7171
+ const kernelLanguage = notebook.metadata?.language_info?.name?.trim() || notebook.metadata?.kernelspec?.language?.trim() || "";
7172
+ const kernelDisplay = notebook.metadata?.kernelspec?.display_name?.trim() || "";
7173
+ let notebookTitle = typeof notebook.metadata?.title === "string" ? notebook.metadata.title.trim() : "";
7174
+ if (!notebookTitle) {
7175
+ for (const cell of cells) {
7176
+ if (cell.cell_type === "markdown") {
7177
+ const heading2 = firstMarkdownHeading(jupyterCellSource(cell));
7178
+ if (heading2) {
7179
+ notebookTitle = heading2;
7180
+ break;
7181
+ }
7182
+ }
7183
+ }
7184
+ }
7185
+ if (!notebookTitle && input.fileName) {
7186
+ notebookTitle = path7.basename(input.fileName, path7.extname(input.fileName));
7187
+ }
7188
+ const sections = [];
7189
+ let markdownCellCount = 0;
7190
+ let codeCellCount = 0;
7191
+ let outputCount = 0;
7192
+ for (const cell of cells) {
7193
+ const source = jupyterCellSource(cell).trim();
7194
+ if (!source) {
7195
+ continue;
7196
+ }
7197
+ if (cell.cell_type === "markdown") {
7198
+ markdownCellCount += 1;
7199
+ sections.push(source);
7200
+ sections.push("");
7201
+ continue;
7202
+ }
7203
+ if (cell.cell_type === "code") {
7204
+ codeCellCount += 1;
7205
+ const fence = kernelLanguage || "";
7206
+ sections.push(`\`\`\`${fence}`);
7207
+ sections.push(source);
7208
+ sections.push("```");
7209
+ const outputSummary = jupyterOutputSummary(cell.outputs);
7210
+ if (outputSummary) {
7211
+ outputCount += Array.isArray(cell.outputs) ? cell.outputs.length : 0;
7212
+ sections.push("");
7213
+ sections.push("_Output:_");
7214
+ sections.push("");
7215
+ sections.push(outputSummary);
7216
+ }
7217
+ sections.push("");
7218
+ continue;
7219
+ }
7220
+ sections.push(source);
7221
+ sections.push("");
7222
+ }
7223
+ const heading = notebookTitle ? [`# ${notebookTitle}`, ""] : [];
7224
+ const extractedText = [
7225
+ ...heading,
7226
+ `Jupyter Notebook (${cells.length} cell${cells.length === 1 ? "" : "s"}, kernel: ${kernelDisplay || kernelLanguage || "unknown"})`,
7227
+ "",
7228
+ ...sections
7229
+ ].join("\n").trim();
7230
+ const metadata = {
7231
+ cell_count: String(cells.length),
7232
+ markdown_cells: String(markdownCellCount),
7233
+ code_cells: String(codeCellCount),
7234
+ output_count: String(outputCount)
7235
+ };
7236
+ if (kernelLanguage) {
7237
+ metadata.kernel_language = kernelLanguage;
7238
+ }
7239
+ if (kernelDisplay) {
7240
+ metadata.kernel_display_name = kernelDisplay;
7241
+ }
7242
+ if (notebook.nbformat !== void 0) {
7243
+ metadata.nbformat = `${notebook.nbformat}${notebook.nbformat_minor !== void 0 ? `.${notebook.nbformat_minor}` : ""}`;
7244
+ }
7245
+ return {
7246
+ title: notebookTitle || void 0,
7247
+ extractedText: extractedText || void 0,
7248
+ artifact: {
7249
+ ...extractionMetadata("jupyter", input.mimeType, "jupyter_text"),
7250
+ metadata
7251
+ }
5431
7252
  };
5432
7253
  } catch (error) {
5433
7254
  return {
5434
7255
  artifact: {
5435
- ...extractionMetadata("docx", input.mimeType, "docx_text"),
5436
- warnings: [`DOCX text extraction failed: ${error instanceof Error ? truncate(error.message, 240) : "unknown error"}`]
7256
+ ...extractionMetadata("jupyter", input.mimeType, "jupyter_text"),
7257
+ warnings: [`Jupyter notebook extraction failed: ${error instanceof Error ? truncate(error.message, 240) : "unknown error"}`]
5437
7258
  }
5438
7259
  };
5439
7260
  }
@@ -5488,7 +7309,7 @@ async function extractCsvText(input) {
5488
7309
  };
5489
7310
  }
5490
7311
  }
5491
- async function extractXlsxText(input) {
7312
+ async function extractSpreadsheetWorkbook(input, sourceKind, extractor) {
5492
7313
  try {
5493
7314
  const XLSX = await import("xlsx");
5494
7315
  const workbook = XLSX.read(input.bytes, { type: "buffer", cellFormula: false, cellHTML: false, cellStyles: false });
@@ -5529,7 +7350,7 @@ async function extractXlsxText(input) {
5529
7350
  title,
5530
7351
  extractedText,
5531
7352
  artifact: {
5532
- ...extractionMetadata("xlsx", input.mimeType, "xlsx_text"),
7353
+ ...extractionMetadata(sourceKind, input.mimeType, extractor),
5533
7354
  metadata,
5534
7355
  warnings
5535
7356
  }
@@ -5537,12 +7358,20 @@ async function extractXlsxText(input) {
5537
7358
  } catch (error) {
5538
7359
  return {
5539
7360
  artifact: {
5540
- ...extractionMetadata("xlsx", input.mimeType, "xlsx_text"),
5541
- warnings: [`XLSX extraction failed: ${error instanceof Error ? truncate(error.message, 240) : "unknown error"}`]
7361
+ ...extractionMetadata(sourceKind, input.mimeType, extractor),
7362
+ warnings: [
7363
+ `${sourceKind.toUpperCase()} extraction failed: ${error instanceof Error ? truncate(error.message, 240) : "unknown error"}`
7364
+ ]
5542
7365
  }
5543
7366
  };
5544
7367
  }
5545
7368
  }
7369
+ async function extractXlsxText(input) {
7370
+ return extractSpreadsheetWorkbook(input, "xlsx", "xlsx_text");
7371
+ }
7372
+ async function extractOdsText(input) {
7373
+ return extractSpreadsheetWorkbook(input, "ods", "ods_text");
7374
+ }
5546
7375
  async function extractPptxText(input) {
5547
7376
  try {
5548
7377
  const archive = unzipSync(new Uint8Array(input.bytes));
@@ -5661,7 +7490,7 @@ async function extractEpubChapters(input) {
5661
7490
  if (!markdown) {
5662
7491
  continue;
5663
7492
  }
5664
- const chapterTitle = firstHtmlHeading(html) || markdown.match(/^#\s+(.+)$/m)?.[1]?.trim() || item.href;
7493
+ const chapterTitle = firstHtmlHeading(html) || item.href;
5665
7494
  const normalizedTitle = normalizeWhitespace(chapterTitle);
5666
7495
  if (!normalizedTitle || /^table of contents$/i.test(normalizedTitle)) {
5667
7496
  continue;
@@ -5836,25 +7665,666 @@ function looksLikeSlackEntries(entries) {
5836
7665
  const hasChannelDayFiles = all.some((entry) => /^[^/]+\/\d{4}-\d{2}-\d{2}\.json$/i.test(entry));
5837
7666
  return hasChannelsIndex && hasChannelDayFiles;
5838
7667
  }
5839
- function slackEntriesFromChannelIndex(raw, usersById) {
5840
- const entries = /* @__PURE__ */ new Map();
5841
- if (!Array.isArray(raw)) {
5842
- return entries;
7668
+ function slackEntriesFromChannelIndex(raw, usersById) {
7669
+ const entries = /* @__PURE__ */ new Map();
7670
+ if (!Array.isArray(raw)) {
7671
+ return entries;
7672
+ }
7673
+ for (const item of raw) {
7674
+ if (!item || typeof item !== "object") {
7675
+ continue;
7676
+ }
7677
+ const value = item;
7678
+ const id = normalizeWhitespace(value.id ?? "");
7679
+ const title = normalizeWhitespace(value.name ?? "");
7680
+ if (!title) {
7681
+ continue;
7682
+ }
7683
+ const members = (Array.isArray(value.members) ? value.members : value.user ? [value.user] : []).map((member) => slackFormatSpeakerId(member, usersById)).filter(Boolean);
7684
+ entries.set(title, { id, title, members });
7685
+ }
7686
+ return entries;
7687
+ }
7688
+ function parseOdfMetadata(bytes) {
7689
+ try {
7690
+ const archive = unzipSync(new Uint8Array(bytes));
7691
+ const metaXml = zipEntryText(archive, "meta.xml");
7692
+ if (!metaXml) {
7693
+ return void 0;
7694
+ }
7695
+ const document = parseXmlDocument(metaXml);
7696
+ const valuesByLocalName = /* @__PURE__ */ new Map();
7697
+ for (const node of Array.from(document.getElementsByTagName("*"))) {
7698
+ const localName = node.localName?.trim().toLowerCase();
7699
+ const text = normalizeWhitespace(node.textContent ?? "");
7700
+ if (!localName || !text || valuesByLocalName.has(localName)) {
7701
+ continue;
7702
+ }
7703
+ valuesByLocalName.set(localName, text);
7704
+ }
7705
+ const metadata = {};
7706
+ const mappings = [
7707
+ ["title", "title"],
7708
+ ["author", "creator"],
7709
+ ["subject", "subject"],
7710
+ ["description", "description"],
7711
+ ["keywords", "keyword"],
7712
+ ["initial_creator", "initial-creator"],
7713
+ ["created", "creation-date"],
7714
+ ["modified", "date"]
7715
+ ];
7716
+ for (const [targetKey, sourceKey] of mappings) {
7717
+ const value = valuesByLocalName.get(sourceKey);
7718
+ if (value) {
7719
+ metadata[targetKey] = value;
7720
+ }
7721
+ }
7722
+ return Object.keys(metadata).length ? metadata : void 0;
7723
+ } catch {
7724
+ return void 0;
7725
+ }
7726
+ }
7727
+ function collectOdfTextNodes(contentXml) {
7728
+ const document = parseXmlDocument(contentXml);
7729
+ const nodes = [];
7730
+ for (const node of Array.from(document.getElementsByTagName("*"))) {
7731
+ const localName = node.localName ?? "";
7732
+ if (localName === "h") {
7733
+ const level = Number.parseInt(node.getAttribute("text:outline-level") ?? "1", 10);
7734
+ const text = normalizeWhitespace(node.textContent ?? "");
7735
+ if (text) {
7736
+ nodes.push({ heading: Number.isFinite(level) && level > 0 ? level : 1, text });
7737
+ }
7738
+ continue;
7739
+ }
7740
+ if (localName === "p" || localName === "list-item") {
7741
+ if (node.closest?.("h")) {
7742
+ continue;
7743
+ }
7744
+ const text = normalizeWhitespace(node.textContent ?? "");
7745
+ if (text) {
7746
+ nodes.push({ text });
7747
+ }
7748
+ }
7749
+ }
7750
+ return nodes;
7751
+ }
7752
+ function renderOdfTextNodes(nodes) {
7753
+ const lines = [];
7754
+ for (const node of nodes) {
7755
+ if (node.heading) {
7756
+ lines.push("");
7757
+ lines.push(`${"#".repeat(Math.min(node.heading, 6))} ${node.text}`);
7758
+ lines.push("");
7759
+ continue;
7760
+ }
7761
+ lines.push(node.text);
7762
+ lines.push("");
7763
+ }
7764
+ return lines.join("\n").trim();
7765
+ }
7766
+ async function extractOdtText(input) {
7767
+ try {
7768
+ const archive = unzipSync(new Uint8Array(input.bytes));
7769
+ const contentXml = zipEntryText(archive, "content.xml");
7770
+ if (!contentXml) {
7771
+ throw new Error("Missing content.xml");
7772
+ }
7773
+ const metadata = parseOdfMetadata(input.bytes);
7774
+ const textNodes = collectOdfTextNodes(contentXml);
7775
+ const headingCount = textNodes.filter((node) => node.heading).length;
7776
+ const paragraphCount = textNodes.filter((node) => !node.heading).length;
7777
+ const title = metadata?.title || textNodes.find((node) => node.heading === 1)?.text || (input.fileName ? path7.basename(input.fileName, path7.extname(input.fileName)) : void 0);
7778
+ const body = renderOdfTextNodes(textNodes);
7779
+ const extractedText = [title ? `# ${title}` : null, "", body].filter((item) => item !== null).join("\n").trim();
7780
+ return {
7781
+ title,
7782
+ extractedText: extractedText || void 0,
7783
+ artifact: {
7784
+ ...extractionMetadata("odt", input.mimeType, "odt_text"),
7785
+ metadata: {
7786
+ ...metadata ?? {},
7787
+ heading_count: String(headingCount),
7788
+ paragraph_count: String(paragraphCount)
7789
+ }
7790
+ }
7791
+ };
7792
+ } catch (error) {
7793
+ return {
7794
+ artifact: {
7795
+ ...extractionMetadata("odt", input.mimeType, "odt_text"),
7796
+ warnings: [`ODT extraction failed: ${error instanceof Error ? truncate(error.message, 240) : "unknown error"}`]
7797
+ }
7798
+ };
7799
+ }
7800
+ }
7801
+ async function extractOdpText(input) {
7802
+ try {
7803
+ const archive = unzipSync(new Uint8Array(input.bytes));
7804
+ const contentXml = zipEntryText(archive, "content.xml");
7805
+ if (!contentXml) {
7806
+ throw new Error("Missing content.xml");
7807
+ }
7808
+ const metadata = parseOdfMetadata(input.bytes);
7809
+ const document = parseXmlDocument(contentXml);
7810
+ const pages = Array.from(document.getElementsByTagName("*")).filter((node) => node.localName === "page");
7811
+ const slideSections = [];
7812
+ pages.slice(0, 60).forEach((page, index) => {
7813
+ const slideName = page.getAttribute("draw:name") ?? `Slide ${index + 1}`;
7814
+ const text = normalizeWhitespace(page.textContent ?? "");
7815
+ slideSections.push(`## Slide ${index + 1}: ${slideName}`);
7816
+ if (text) {
7817
+ slideSections.push(text);
7818
+ }
7819
+ slideSections.push("");
7820
+ });
7821
+ const title = metadata?.title || (input.fileName ? path7.basename(input.fileName, path7.extname(input.fileName)) : void 0);
7822
+ const extractedText = [title ? `# ${title}` : null, `Slides: ${pages.length}`, "", ...slideSections].filter((item) => Boolean(item)).join("\n").trim();
7823
+ const warnings = pages.length > 60 ? ["ODP extraction truncated to the first 60 slides."] : void 0;
7824
+ return {
7825
+ title,
7826
+ extractedText: extractedText || void 0,
7827
+ artifact: {
7828
+ ...extractionMetadata("odp", input.mimeType, "odp_text"),
7829
+ metadata: {
7830
+ ...metadata ?? {},
7831
+ slide_count: String(pages.length)
7832
+ },
7833
+ warnings
7834
+ }
7835
+ };
7836
+ } catch (error) {
7837
+ return {
7838
+ artifact: {
7839
+ ...extractionMetadata("odp", input.mimeType, "odp_text"),
7840
+ warnings: [`ODP extraction failed: ${error instanceof Error ? truncate(error.message, 240) : "unknown error"}`]
7841
+ }
7842
+ };
7843
+ }
7844
+ }
7845
+ function inferStructuredFormat(mimeType, fileName) {
7846
+ const lower = (fileName ?? "").toLowerCase();
7847
+ if (lower.endsWith(".jsonc") || lower.endsWith(".json") || lower.endsWith(".json5") || mimeType === "application/json" || mimeType === "application/json5") {
7848
+ return "json";
7849
+ }
7850
+ if (lower.endsWith(".yaml") || lower.endsWith(".yml") || mimeType === "application/yaml" || mimeType === "application/x-yaml") {
7851
+ return "yaml";
7852
+ }
7853
+ if (lower.endsWith(".toml") || mimeType === "application/toml") {
7854
+ return "toml";
7855
+ }
7856
+ if (lower.endsWith(".xml") || mimeType === "application/xml" || mimeType === "text/xml") {
7857
+ return "xml";
7858
+ }
7859
+ if (lower.endsWith(".ini") || lower.endsWith(".conf") || lower.endsWith(".cfg")) {
7860
+ return "ini";
7861
+ }
7862
+ if (lower.endsWith(".env")) {
7863
+ return "env";
7864
+ }
7865
+ if (lower.endsWith(".properties")) {
7866
+ return "properties";
7867
+ }
7868
+ return null;
7869
+ }
7870
+ function parseEnvFile(text) {
7871
+ const result = {};
7872
+ for (const rawLine of text.split(/\r?\n/)) {
7873
+ const line = rawLine.trim();
7874
+ if (!line || line.startsWith("#")) {
7875
+ continue;
7876
+ }
7877
+ const eqIndex = line.indexOf("=");
7878
+ if (eqIndex <= 0) {
7879
+ continue;
7880
+ }
7881
+ const key = line.slice(0, eqIndex).trim();
7882
+ let value = line.slice(eqIndex + 1).trim();
7883
+ if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
7884
+ value = value.slice(1, -1);
7885
+ }
7886
+ result[key] = value;
7887
+ }
7888
+ return result;
7889
+ }
7890
+ function parsePropertiesFile(text) {
7891
+ const result = {};
7892
+ for (const rawLine of text.split(/\r?\n/)) {
7893
+ const line = rawLine.trim();
7894
+ if (!line || line.startsWith("#") || line.startsWith("!")) {
7895
+ continue;
7896
+ }
7897
+ let sep = line.indexOf("=");
7898
+ if (sep < 0) {
7899
+ sep = line.indexOf(":");
7900
+ }
7901
+ if (sep <= 0) {
7902
+ continue;
7903
+ }
7904
+ const key = line.slice(0, sep).trim();
7905
+ const value = line.slice(sep + 1).trim();
7906
+ result[key] = value;
7907
+ }
7908
+ return result;
7909
+ }
7910
+ function parseXmlToSchema(text) {
7911
+ const document = parseXmlDocument(text);
7912
+ const root = document.documentElement;
7913
+ if (!root) {
7914
+ return {};
7915
+ }
7916
+ const childCounts = /* @__PURE__ */ new Map();
7917
+ for (const child of Array.from(root.children)) {
7918
+ const name = child.tagName || child.localName || "";
7919
+ if (!name) {
7920
+ continue;
7921
+ }
7922
+ childCounts.set(name, (childCounts.get(name) ?? 0) + 1);
7923
+ }
7924
+ const result = {};
7925
+ for (const [name, count] of childCounts.entries()) {
7926
+ result[name] = { count };
7927
+ }
7928
+ return { [root.tagName || "root"]: result };
7929
+ }
7930
+ function describeJsonShape(value) {
7931
+ if (value === null) {
7932
+ return { type: "null", size: 0, depth: 0 };
7933
+ }
7934
+ if (Array.isArray(value)) {
7935
+ const depths = value.map((entry) => describeJsonShape(entry).depth);
7936
+ return { type: "array", size: value.length, depth: 1 + (depths.length ? Math.max(...depths) : 0) };
7937
+ }
7938
+ if (typeof value === "object") {
7939
+ const entries = Object.entries(value);
7940
+ const depths = entries.map(([, v]) => describeJsonShape(v).depth);
7941
+ return { type: "object", size: entries.length, depth: 1 + (depths.length ? Math.max(...depths) : 0) };
7942
+ }
7943
+ return { type: typeof value, size: 0, depth: 0 };
7944
+ }
7945
+ function describeTopLevelSchema(value) {
7946
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
7947
+ const shape = describeJsonShape(value);
7948
+ return [`(root) ${shape.type}${shape.size ? ` (${shape.size})` : ""}`];
7949
+ }
7950
+ const entries = Object.entries(value);
7951
+ return entries.slice(0, 20).map(([key, child]) => {
7952
+ const shape = describeJsonShape(child);
7953
+ const sizeHint = shape.type === "array" ? ` (${shape.size} items)` : shape.type === "object" ? ` (${shape.size} keys)` : "";
7954
+ return `${key}: ${shape.type}${sizeHint}`;
7955
+ });
7956
+ }
7957
+ async function parseStructuredPayload(bytes, format) {
7958
+ const text = decodeTextBytes(bytes);
7959
+ if (format === "json") {
7960
+ const cleaned = text.replace(/^\uFEFF/, "");
7961
+ return { format, value: JSON.parse(cleaned) };
7962
+ }
7963
+ if (format === "yaml") {
7964
+ const yamlModule = await import("yaml");
7965
+ return { format, value: yamlModule.parse(text) };
7966
+ }
7967
+ if (format === "toml") {
7968
+ const tomlModule = await import("smol-toml");
7969
+ return { format, value: tomlModule.parse(text) };
7970
+ }
7971
+ if (format === "xml") {
7972
+ return { format, value: parseXmlToSchema(text) };
7973
+ }
7974
+ if (format === "ini") {
7975
+ try {
7976
+ const tomlModule = await import("smol-toml");
7977
+ return { format, value: tomlModule.parse(text) };
7978
+ } catch {
7979
+ return { format, value: parsePropertiesFile(text) };
7980
+ }
7981
+ }
7982
+ if (format === "env") {
7983
+ return { format, value: parseEnvFile(text) };
7984
+ }
7985
+ return { format, value: parsePropertiesFile(text) };
7986
+ }
7987
+ async function extractStructuredData(input) {
7988
+ const format = inferStructuredFormat(input.mimeType, input.fileName);
7989
+ if (!format) {
7990
+ return {
7991
+ artifact: {
7992
+ ...extractionMetadata("data", input.mimeType, "structured_data"),
7993
+ warnings: ["Structured data extraction skipped: format not recognized."]
7994
+ }
7995
+ };
7996
+ }
7997
+ try {
7998
+ const { value } = await parseStructuredPayload(input.bytes, format);
7999
+ const shape = describeJsonShape(value);
8000
+ const schemaLines = describeTopLevelSchema(value);
8001
+ const previewText = decodeTextBytes(input.bytes);
8002
+ const previewLines = previewText.split(/\r?\n/).slice(0, 40);
8003
+ const truncated = previewText.split(/\r?\n/).length > previewLines.length;
8004
+ const title = input.fileName ? path7.basename(input.fileName, path7.extname(input.fileName)) : void 0;
8005
+ const extractedText = [
8006
+ title ? `# ${title}` : null,
8007
+ `Format: ${format.toUpperCase()}`,
8008
+ `Top-level: ${shape.type}`,
8009
+ shape.type === "object" || shape.type === "array" ? `Size: ${shape.size}` : null,
8010
+ `Nested depth: ${shape.depth}`,
8011
+ "",
8012
+ "## Schema",
8013
+ "",
8014
+ ...schemaLines.map((entry) => `- ${entry}`),
8015
+ "",
8016
+ "## Preview",
8017
+ "",
8018
+ `\`\`\`${format}`,
8019
+ ...previewLines,
8020
+ truncated ? "\u2026" : null,
8021
+ "```"
8022
+ ].filter((item) => item !== null).join("\n").trim();
8023
+ return {
8024
+ title,
8025
+ extractedText,
8026
+ artifact: {
8027
+ ...extractionMetadata("data", input.mimeType, "structured_data"),
8028
+ metadata: {
8029
+ format,
8030
+ top_level_type: shape.type,
8031
+ top_level_size: String(shape.size),
8032
+ nested_depth: String(shape.depth)
8033
+ }
8034
+ }
8035
+ };
8036
+ } catch (error) {
8037
+ return {
8038
+ artifact: {
8039
+ ...extractionMetadata("data", input.mimeType, "structured_data"),
8040
+ warnings: [`Structured data extraction failed: ${error instanceof Error ? truncate(error.message, 240) : "unknown error"}`]
8041
+ }
8042
+ };
8043
+ }
8044
+ }
8045
+ function formatBibCreator(creator) {
8046
+ if (creator.name) {
8047
+ return creator.name;
8048
+ }
8049
+ const parts = [creator.prefix, creator.firstName, creator.lastName, creator.suffix].filter(Boolean);
8050
+ return parts.join(" ");
8051
+ }
8052
+ function bibFieldString(value) {
8053
+ if (value == null) {
8054
+ return "";
8055
+ }
8056
+ if (typeof value === "string") {
8057
+ return value.trim();
8058
+ }
8059
+ if (typeof value === "number" || typeof value === "boolean") {
8060
+ return String(value);
8061
+ }
8062
+ if (Array.isArray(value)) {
8063
+ return value.map((item) => bibFieldString(item)).filter(Boolean).join(", ");
8064
+ }
8065
+ if (typeof value === "object") {
8066
+ return bibFieldString(value.name ?? "");
8067
+ }
8068
+ return String(value);
8069
+ }
8070
+ async function extractBibTeXText(input) {
8071
+ try {
8072
+ const bibtex = await import("@retorquere/bibtex-parser");
8073
+ const text = decodeTextBytes(input.bytes);
8074
+ const library = bibtex.parse(text);
8075
+ const entries = Array.isArray(library.entries) ? library.entries : [];
8076
+ const citationTypes = /* @__PURE__ */ new Map();
8077
+ for (const entry of entries) {
8078
+ const type = (entry.type ?? "misc").toLowerCase();
8079
+ citationTypes.set(type, (citationTypes.get(type) ?? 0) + 1);
8080
+ }
8081
+ const entrySections = [];
8082
+ for (const entry of entries.slice(0, 200)) {
8083
+ const fields = entry.fields ?? {};
8084
+ const title2 = bibFieldString(fields.title);
8085
+ const authorList = Array.isArray(fields.author) ? fields.author.map((creator) => formatBibCreator(creator)).filter(Boolean) : bibFieldString(fields.author).split(/\s+and\s+/i).filter(Boolean);
8086
+ const editorList = Array.isArray(fields.editor) ? fields.editor.map((creator) => formatBibCreator(creator)).filter(Boolean) : [];
8087
+ const year = bibFieldString(fields.year ?? fields.date ?? "");
8088
+ const journal = bibFieldString(fields.journal ?? fields.booktitle ?? fields.publisher ?? "");
8089
+ const doi = bibFieldString(fields.doi);
8090
+ const url = bibFieldString(fields.url);
8091
+ const credit = authorList.length ? authorList.join(", ") : editorList.length ? `${editorList.join(", ")} (eds.)` : "Unknown";
8092
+ const descriptorParts = [credit];
8093
+ if (year) {
8094
+ descriptorParts.push(year);
8095
+ }
8096
+ const descriptor = descriptorParts.join(", ");
8097
+ const trailing = [];
8098
+ if (journal) {
8099
+ trailing.push(journal);
8100
+ }
8101
+ if (doi) {
8102
+ trailing.push(`doi:${doi}`);
8103
+ }
8104
+ if (url) {
8105
+ trailing.push(url);
8106
+ }
8107
+ const trailingText = trailing.length ? ` \u2014 ${trailing.join(", ")}` : "";
8108
+ entrySections.push(`- [${entry.key}] ${title2 || "(untitled)"} (${descriptor})${trailingText}`);
8109
+ }
8110
+ const totalEntries = entries.length;
8111
+ const truncated = entries.length > 200;
8112
+ const typeSummary = [...citationTypes.entries()].sort((left, right) => right[1] - left[1] || left[0].localeCompare(right[0])).map(([type, count]) => `${type} (${count})`).join(", ");
8113
+ const title = input.fileName ? path7.basename(input.fileName, path7.extname(input.fileName)) : "BibTeX library";
8114
+ const extractedText = [
8115
+ `# ${title}`,
8116
+ "",
8117
+ `BibTeX library with ${totalEntries} entr${totalEntries === 1 ? "y" : "ies"}.`,
8118
+ typeSummary ? `Citation types: ${typeSummary}.` : null,
8119
+ "",
8120
+ "## Entries",
8121
+ "",
8122
+ ...entrySections,
8123
+ truncated ? `
8124
+ _Preview truncated to the first 200 entries._` : null
8125
+ ].filter((item) => item !== null).join("\n").trim();
8126
+ const warnings = library.errors?.length ? [`BibTeX parser reported ${library.errors.length} parse error(s).`] : void 0;
8127
+ return {
8128
+ title,
8129
+ extractedText,
8130
+ artifact: {
8131
+ ...extractionMetadata("bibtex", input.mimeType, "bibtex_text"),
8132
+ metadata: {
8133
+ entry_count: String(totalEntries),
8134
+ citation_types: [...citationTypes.keys()].sort().join(",")
8135
+ },
8136
+ warnings
8137
+ }
8138
+ };
8139
+ } catch (error) {
8140
+ return {
8141
+ artifact: {
8142
+ ...extractionMetadata("bibtex", input.mimeType, "bibtex_text"),
8143
+ warnings: [`BibTeX extraction failed: ${error instanceof Error ? truncate(error.message, 240) : "unknown error"}`]
8144
+ }
8145
+ };
8146
+ }
8147
+ }
8148
+ async function extractRtfText(input) {
8149
+ try {
8150
+ const rtfParser = await import("rtf-parser");
8151
+ const parseString = rtfParser.string ?? rtfParser.default?.string;
8152
+ if (typeof parseString !== "function") {
8153
+ throw new Error("rtf-parser did not expose a string parser.");
8154
+ }
8155
+ const rtfText = decodeTextBytes(input.bytes);
8156
+ const document = await new Promise((resolve, reject) => {
8157
+ parseString(rtfText, (err, doc) => {
8158
+ if (err || !doc) {
8159
+ reject(err ?? new Error("RTF parse returned no document"));
8160
+ return;
8161
+ }
8162
+ resolve(doc);
8163
+ });
8164
+ });
8165
+ const paragraphs = [];
8166
+ for (const paragraph of document.content ?? []) {
8167
+ const spans = paragraph.content ?? [];
8168
+ const text = normalizeWhitespace(spans.map((span) => span.value ?? "").join(""));
8169
+ if (text) {
8170
+ paragraphs.push(text);
8171
+ }
8172
+ }
8173
+ const title = input.fileName ? path7.basename(input.fileName, path7.extname(input.fileName)) : void 0;
8174
+ const extractedText = [title ? `# ${title}` : null, "", ...paragraphs].filter((item) => item !== null).join("\n\n").trim();
8175
+ return {
8176
+ title,
8177
+ extractedText: extractedText || void 0,
8178
+ artifact: {
8179
+ ...extractionMetadata("rtf", input.mimeType, "rtf_text"),
8180
+ metadata: {
8181
+ paragraph_count: String(paragraphs.length)
8182
+ }
8183
+ }
8184
+ };
8185
+ } catch (error) {
8186
+ return {
8187
+ artifact: {
8188
+ ...extractionMetadata("rtf", input.mimeType, "rtf_text"),
8189
+ warnings: [`RTF extraction failed: ${error instanceof Error ? truncate(error.message, 240) : "unknown error"}`]
8190
+ }
8191
+ };
8192
+ }
8193
+ }
8194
+ function collectOrgNodeText(node) {
8195
+ if (typeof node.value === "string") {
8196
+ return node.value;
5843
8197
  }
5844
- for (const item of raw) {
5845
- if (!item || typeof item !== "object") {
5846
- continue;
8198
+ if (!Array.isArray(node.children)) {
8199
+ return "";
8200
+ }
8201
+ return node.children.map((child) => collectOrgNodeText(child)).join("");
8202
+ }
8203
+ function renderOrgNode(node, lines) {
8204
+ if (node.type === "headline") {
8205
+ const depth = Math.min(Math.max(node.level ?? 1, 1), 6);
8206
+ const keyword = node.keyword ? `${node.keyword} ` : "";
8207
+ const tags = node.tags?.length ? ` \`${node.tags.join(":")}\`` : "";
8208
+ const text = normalizeWhitespace(collectOrgNodeText(node));
8209
+ lines.push("");
8210
+ lines.push(`${"#".repeat(depth)} ${keyword}${text}${tags}`.trim());
8211
+ lines.push("");
8212
+ return;
8213
+ }
8214
+ if (node.type === "paragraph") {
8215
+ const text = normalizeWhitespace(collectOrgNodeText(node));
8216
+ if (text) {
8217
+ lines.push(text);
8218
+ lines.push("");
5847
8219
  }
5848
- const value = item;
5849
- const id = normalizeWhitespace(value.id ?? "");
5850
- const title = normalizeWhitespace(value.name ?? "");
5851
- if (!title) {
5852
- continue;
8220
+ return;
8221
+ }
8222
+ if (node.type === "list") {
8223
+ for (const child of node.children ?? []) {
8224
+ if (child.type === "list.item") {
8225
+ const text = normalizeWhitespace(collectOrgNodeText(child));
8226
+ if (text) {
8227
+ lines.push(`- ${text}`);
8228
+ }
8229
+ }
5853
8230
  }
5854
- const members = (Array.isArray(value.members) ? value.members : value.user ? [value.user] : []).map((member) => slackFormatSpeakerId(member, usersById)).filter(Boolean);
5855
- entries.set(title, { id, title, members });
8231
+ lines.push("");
8232
+ return;
8233
+ }
8234
+ if (node.type === "block") {
8235
+ const name = node.name ?? "";
8236
+ const body = typeof node.value === "string" ? node.value.trimEnd() : "";
8237
+ if (body) {
8238
+ lines.push(`\`\`\`${name === "src" ? "" : name.toLowerCase()}`);
8239
+ lines.push(body);
8240
+ lines.push("```");
8241
+ lines.push("");
8242
+ }
8243
+ return;
8244
+ }
8245
+ for (const child of node.children ?? []) {
8246
+ renderOrgNode(child, lines);
8247
+ }
8248
+ }
8249
+ async function extractOrgText(input) {
8250
+ try {
8251
+ const orga = await import("orga");
8252
+ const text = decodeTextBytes(input.bytes);
8253
+ const document = orga.parse(text);
8254
+ const properties = document.properties ?? {};
8255
+ const documentTitle = Array.isArray(properties.title) ? properties.title.join(" ") : typeof properties.title === "string" ? properties.title : "";
8256
+ let headlineCount = 0;
8257
+ let todoCount = 0;
8258
+ const walk = (node) => {
8259
+ if (node.type === "headline") {
8260
+ headlineCount += 1;
8261
+ if (node.keyword) {
8262
+ todoCount += 1;
8263
+ }
8264
+ }
8265
+ for (const child of node.children ?? []) {
8266
+ walk(child);
8267
+ }
8268
+ };
8269
+ walk(document);
8270
+ const bodyLines = [];
8271
+ for (const child of document.children ?? []) {
8272
+ renderOrgNode(child, bodyLines);
8273
+ }
8274
+ const title = documentTitle.trim() || (input.fileName ? path7.basename(input.fileName, path7.extname(input.fileName)) : void 0);
8275
+ const extractedText = [title ? `# ${title}` : null, "", ...bodyLines].filter((item) => item !== null).join("\n").trim();
8276
+ return {
8277
+ title,
8278
+ extractedText: extractedText || void 0,
8279
+ artifact: {
8280
+ ...extractionMetadata("org", input.mimeType, "org_text"),
8281
+ metadata: {
8282
+ headline_count: String(headlineCount),
8283
+ todo_count: String(todoCount)
8284
+ }
8285
+ }
8286
+ };
8287
+ } catch (error) {
8288
+ return {
8289
+ artifact: {
8290
+ ...extractionMetadata("org", input.mimeType, "org_text"),
8291
+ warnings: [`Org extraction failed: ${error instanceof Error ? truncate(error.message, 240) : "unknown error"}`]
8292
+ }
8293
+ };
8294
+ }
8295
+ }
8296
+ async function extractAsciiDocText(input) {
8297
+ try {
8298
+ const asciidoctorModule = await import("@asciidoctor/core");
8299
+ const factory = asciidoctorModule.default ?? asciidoctorModule;
8300
+ const processor = factory();
8301
+ const source = decodeTextBytes(input.bytes);
8302
+ const loaded = processor.load(source, { safe: "safe" });
8303
+ const html = processor.convert(source, { safe: "safe", standalone: false });
8304
+ const markdown = htmlToMarkdown(html);
8305
+ const docTitle = (typeof loaded.getTitle === "function" ? loaded.getTitle() : void 0) ?? void 0;
8306
+ const fileTitle = input.fileName ? path7.basename(input.fileName, path7.extname(input.fileName)) : void 0;
8307
+ const title = docTitle?.trim() || fileTitle;
8308
+ const extractedText = [title ? `# ${title}` : null, "", markdown].filter((item) => item !== null).join("\n").trim();
8309
+ return {
8310
+ title,
8311
+ extractedText: extractedText || void 0,
8312
+ artifact: {
8313
+ ...extractionMetadata("asciidoc", input.mimeType, "asciidoc_text"),
8314
+ metadata: {
8315
+ html_size: String(html.length),
8316
+ markdown_size: String(markdown.length)
8317
+ }
8318
+ }
8319
+ };
8320
+ } catch (error) {
8321
+ return {
8322
+ artifact: {
8323
+ ...extractionMetadata("asciidoc", input.mimeType, "asciidoc_text"),
8324
+ warnings: [`AsciiDoc extraction failed: ${error instanceof Error ? truncate(error.message, 240) : "unknown error"}`]
8325
+ }
8326
+ };
5856
8327
  }
5857
- return entries;
5858
8328
  }
5859
8329
  async function extractTranscriptText(input) {
5860
8330
  try {
@@ -6624,7 +9094,7 @@ function inferKind(mimeType, filePath, detectionOptions = {}) {
6624
9094
  if (isTranscriptFilePath(filePath) || mimeType === "application/x-subrip" || mimeType === "text/vtt") {
6625
9095
  return "transcript";
6626
9096
  }
6627
- if (mimeType.includes("markdown")) {
9097
+ if (mimeType.includes("markdown") || filePath.toLowerCase().endsWith(".mdx")) {
6628
9098
  return "markdown";
6629
9099
  }
6630
9100
  if (mimeType.includes("html")) {
@@ -6633,7 +9103,7 @@ function inferKind(mimeType, filePath, detectionOptions = {}) {
6633
9103
  if (mimeType === "application/pdf" || filePath.toLowerCase().endsWith(".pdf")) {
6634
9104
  return "pdf";
6635
9105
  }
6636
- if (mimeType === "application/vnd.openxmlformats-officedocument.wordprocessingml.document" || filePath.toLowerCase().endsWith(".docx")) {
9106
+ if (mimeType === "application/vnd.openxmlformats-officedocument.wordprocessingml.document" || mimeType === "application/vnd.ms-word.document.macroenabled.12" || mimeType === "application/vnd.ms-word.template.macroenabled.12" || mimeType === "application/vnd.openxmlformats-officedocument.wordprocessingml.template" || filePath.toLowerCase().endsWith(".docx") || filePath.toLowerCase().endsWith(".docm") || filePath.toLowerCase().endsWith(".dotx") || filePath.toLowerCase().endsWith(".dotm")) {
6637
9107
  return "docx";
6638
9108
  }
6639
9109
  if (isEmailFilePath(filePath) || mimeType === "message/rfc822" || mimeType === "application/mbox") {
@@ -6648,20 +9118,66 @@ function inferKind(mimeType, filePath, detectionOptions = {}) {
6648
9118
  if (mimeType === "text/csv" || mimeType === "text/tab-separated-values" || filePath.toLowerCase().endsWith(".csv") || filePath.toLowerCase().endsWith(".tsv")) {
6649
9119
  return "csv";
6650
9120
  }
9121
+ if (mimeType === "application/x-ipynb+json" || filePath.toLowerCase().endsWith(".ipynb")) {
9122
+ return "jupyter";
9123
+ }
9124
+ if (mimeType === "application/vnd.oasis.opendocument.text" || filePath.toLowerCase().endsWith(".odt")) {
9125
+ return "odt";
9126
+ }
9127
+ if (mimeType === "application/vnd.oasis.opendocument.presentation" || filePath.toLowerCase().endsWith(".odp")) {
9128
+ return "odp";
9129
+ }
9130
+ if (mimeType === "application/vnd.oasis.opendocument.spreadsheet" || filePath.toLowerCase().endsWith(".ods")) {
9131
+ return "ods";
9132
+ }
9133
+ if (filePath.toLowerCase().endsWith(".bib") || mimeType === "application/x-bibtex") {
9134
+ return "bibtex";
9135
+ }
9136
+ if (mimeType === "application/rtf" || mimeType === "text/rtf" || filePath.toLowerCase().endsWith(".rtf")) {
9137
+ return "rtf";
9138
+ }
9139
+ if (filePath.toLowerCase().endsWith(".org") || mimeType === "text/x-org") {
9140
+ return "org";
9141
+ }
9142
+ if (filePath.toLowerCase().endsWith(".adoc") || filePath.toLowerCase().endsWith(".asciidoc") || mimeType === "text/x-asciidoc") {
9143
+ return "asciidoc";
9144
+ }
9145
+ if (isStructuredDataPath(filePath, mimeType)) {
9146
+ return "data";
9147
+ }
6651
9148
  if (mimeType.startsWith("text/") || isStructuredTextMime(mimeType)) {
6652
9149
  return "text";
6653
9150
  }
6654
- if (mimeType === "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" || filePath.toLowerCase().endsWith(".xlsx")) {
9151
+ if (mimeType === "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" || mimeType === "application/vnd.ms-excel" || mimeType === "application/vnd.ms-excel.sheet.macroenabled.12" || mimeType === "application/vnd.ms-excel.template.macroenabled.12" || mimeType === "application/vnd.openxmlformats-officedocument.spreadsheetml.template" || filePath.toLowerCase().endsWith(".xlsx") || filePath.toLowerCase().endsWith(".xlsm") || filePath.toLowerCase().endsWith(".xltx") || filePath.toLowerCase().endsWith(".xltm") || filePath.toLowerCase().endsWith(".xls")) {
6655
9152
  return "xlsx";
6656
9153
  }
6657
- if (mimeType === "application/vnd.openxmlformats-officedocument.presentationml.presentation" || filePath.toLowerCase().endsWith(".pptx")) {
9154
+ if (mimeType === "application/vnd.openxmlformats-officedocument.presentationml.presentation" || mimeType === "application/vnd.ms-powerpoint.presentation.macroenabled.12" || mimeType === "application/vnd.ms-powerpoint.template.macroenabled.12" || mimeType === "application/vnd.openxmlformats-officedocument.presentationml.template" || filePath.toLowerCase().endsWith(".pptx") || filePath.toLowerCase().endsWith(".pptm") || filePath.toLowerCase().endsWith(".potx") || filePath.toLowerCase().endsWith(".potm")) {
6658
9155
  return "pptx";
6659
9156
  }
6660
- if (mimeType.startsWith("image/")) {
9157
+ if (mimeType.startsWith("image/") || isImagePath(filePath)) {
6661
9158
  return "image";
6662
9159
  }
6663
9160
  return "binary";
6664
9161
  }
9162
+ var IMAGE_EXTENSIONS = /* @__PURE__ */ new Set([
9163
+ ".png",
9164
+ ".jpg",
9165
+ ".jpeg",
9166
+ ".webp",
9167
+ ".gif",
9168
+ ".bmp",
9169
+ ".ico",
9170
+ ".tiff",
9171
+ ".tif",
9172
+ ".heic",
9173
+ ".heif",
9174
+ ".avif",
9175
+ ".jxl",
9176
+ ".svg"
9177
+ ]);
9178
+ function isImagePath(filePath) {
9179
+ return IMAGE_EXTENSIONS.has(path12.extname(filePath).toLowerCase());
9180
+ }
6665
9181
  function isStructuredTextMime(mimeType) {
6666
9182
  switch (mimeType) {
6667
9183
  case "application/json":
@@ -6682,6 +9198,23 @@ function isStructuredTextMime(mimeType) {
6682
9198
  return false;
6683
9199
  }
6684
9200
  }
9201
+ function isStructuredDataPath(filePath, mimeType) {
9202
+ const lower = filePath.toLowerCase();
9203
+ if (lower.endsWith(".yaml") || lower.endsWith(".yml") || lower.endsWith(".toml") || mimeType === "application/toml" || mimeType === "application/yaml" || mimeType === "application/x-yaml") {
9204
+ return true;
9205
+ }
9206
+ if (lower.endsWith(".xml") || lower.endsWith(".ini") || lower.endsWith(".env") || lower.endsWith(".properties") || lower.endsWith(".conf") || lower.endsWith(".cfg") || mimeType === "application/xml" || mimeType === "text/xml") {
9207
+ return true;
9208
+ }
9209
+ if (lower.endsWith(".json") || lower.endsWith(".jsonc") || lower.endsWith(".json5") || mimeType === "application/json" || mimeType === "application/json5") {
9210
+ const base = path12.basename(lower);
9211
+ if (base === "package.json" || base === "package-lock.json" || base === "tsconfig.json" || base === "pnpm-lock.yaml") {
9212
+ return false;
9213
+ }
9214
+ return true;
9215
+ }
9216
+ return false;
9217
+ }
6685
9218
  async function localCodeDetectionOptions(absolutePath, payloadBytes) {
6686
9219
  if (path12.extname(absolutePath)) {
6687
9220
  return {};
@@ -6723,8 +9256,7 @@ function titleFromText(fallback, content, filePath) {
6723
9256
  return rstTitle;
6724
9257
  }
6725
9258
  }
6726
- const heading = content.match(/^#\s+(.+)$/m)?.[1]?.trim();
6727
- return heading || fallback;
9259
+ return firstMarkdownHeading(content) ?? fallback;
6728
9260
  }
6729
9261
  function guessMimeType(target) {
6730
9262
  if (isRstFilePath(target)) {
@@ -6888,8 +9420,7 @@ function normalizeRstExtractedText(content) {
6888
9420
  }
6889
9421
  function titleFromRst(fallback, content) {
6890
9422
  const normalized = normalizeRstExtractedText(content);
6891
- const heading = normalized.match(/^#+\s+(.+)$/m)?.[1]?.trim();
6892
- return heading || fallback;
9423
+ return firstMarkdownHeading(normalized) ?? fallback;
6893
9424
  }
6894
9425
  function extractedTextForPlainSource(filePath, sourceKind, content) {
6895
9426
  if (sourceKind === "text" && isRstFilePath(filePath)) {
@@ -8339,6 +10870,60 @@ async function prepareFileInputs(rootDir, absoluteInput, repoRoot, sourceClass)
8339
10870
  title = extracted.title?.trim() || title;
8340
10871
  extractedText = extracted.extractedText;
8341
10872
  extractionArtifact = extracted.artifact;
10873
+ } else if (sourceKind === "jupyter") {
10874
+ title = path12.basename(absoluteInput, path12.extname(absoluteInput));
10875
+ const extracted = await extractJupyterNotebook({ mimeType, bytes: payloadBytes, fileName: absoluteInput });
10876
+ title = extracted.title?.trim() || title;
10877
+ extractedText = extracted.extractedText;
10878
+ extractionArtifact = extracted.artifact;
10879
+ } else if (sourceKind === "odt") {
10880
+ title = path12.basename(absoluteInput, path12.extname(absoluteInput));
10881
+ const extracted = await extractOdtText({ mimeType, bytes: payloadBytes, fileName: absoluteInput });
10882
+ title = extracted.title?.trim() || title;
10883
+ extractedText = extracted.extractedText;
10884
+ extractionArtifact = extracted.artifact;
10885
+ } else if (sourceKind === "odp") {
10886
+ title = path12.basename(absoluteInput, path12.extname(absoluteInput));
10887
+ const extracted = await extractOdpText({ mimeType, bytes: payloadBytes, fileName: absoluteInput });
10888
+ title = extracted.title?.trim() || title;
10889
+ extractedText = extracted.extractedText;
10890
+ extractionArtifact = extracted.artifact;
10891
+ } else if (sourceKind === "ods") {
10892
+ title = path12.basename(absoluteInput, path12.extname(absoluteInput));
10893
+ const extracted = await extractOdsText({ mimeType, bytes: payloadBytes, fileName: absoluteInput });
10894
+ title = extracted.title?.trim() || title;
10895
+ extractedText = extracted.extractedText;
10896
+ extractionArtifact = extracted.artifact;
10897
+ } else if (sourceKind === "data") {
10898
+ title = path12.basename(absoluteInput, path12.extname(absoluteInput));
10899
+ const extracted = await extractStructuredData({ mimeType, bytes: payloadBytes, fileName: absoluteInput });
10900
+ title = extracted.title?.trim() || title;
10901
+ extractedText = extracted.extractedText;
10902
+ extractionArtifact = extracted.artifact;
10903
+ } else if (sourceKind === "bibtex") {
10904
+ title = path12.basename(absoluteInput, path12.extname(absoluteInput));
10905
+ const extracted = await extractBibTeXText({ mimeType, bytes: payloadBytes, fileName: absoluteInput });
10906
+ title = extracted.title?.trim() || title;
10907
+ extractedText = extracted.extractedText;
10908
+ extractionArtifact = extracted.artifact;
10909
+ } else if (sourceKind === "rtf") {
10910
+ title = path12.basename(absoluteInput, path12.extname(absoluteInput));
10911
+ const extracted = await extractRtfText({ mimeType, bytes: payloadBytes, fileName: absoluteInput });
10912
+ title = extracted.title?.trim() || title;
10913
+ extractedText = extracted.extractedText;
10914
+ extractionArtifact = extracted.artifact;
10915
+ } else if (sourceKind === "org") {
10916
+ title = path12.basename(absoluteInput, path12.extname(absoluteInput));
10917
+ const extracted = await extractOrgText({ mimeType, bytes: payloadBytes, fileName: absoluteInput });
10918
+ title = extracted.title?.trim() || title;
10919
+ extractedText = extracted.extractedText;
10920
+ extractionArtifact = extracted.artifact;
10921
+ } else if (sourceKind === "asciidoc") {
10922
+ title = path12.basename(absoluteInput, path12.extname(absoluteInput));
10923
+ const extracted = await extractAsciiDocText({ mimeType, bytes: payloadBytes, fileName: absoluteInput });
10924
+ title = extracted.title?.trim() || title;
10925
+ extractedText = extracted.extractedText;
10926
+ extractionArtifact = extracted.artifact;
8342
10927
  } else if (sourceKind === "epub") {
8343
10928
  title = path12.basename(absoluteInput, path12.extname(absoluteInput));
8344
10929
  const extracted = await extractEpubChapters({ mimeType, bytes: payloadBytes, fileName: absoluteInput });
@@ -8665,7 +11250,11 @@ async function collectInboxAttachmentRefs(inputDir, files) {
8665
11250
  for (const absolutePath of files) {
8666
11251
  const mimeType = guessMimeType(absolutePath);
8667
11252
  const detectionOptions = await localCodeDetectionOptions(absolutePath);
8668
- const sourceKind = inferKind(mimeType, absolutePath, detectionOptions);
11253
+ let sourceKind = inferKind(mimeType, absolutePath, detectionOptions);
11254
+ const lowerExt = path12.extname(absolutePath).toLowerCase();
11255
+ if ((lowerExt === ".html" || lowerExt === ".htm") && sourceKind === "code") {
11256
+ sourceKind = "html";
11257
+ }
8669
11258
  if (sourceKind !== "markdown" && sourceKind !== "html") {
8670
11259
  continue;
8671
11260
  }
@@ -9009,7 +11598,11 @@ async function importInbox(rootDir, inputDir) {
9009
11598
  const mimeType = guessMimeType(absolutePath);
9010
11599
  const detectionOptions = await localCodeDetectionOptions(absolutePath);
9011
11600
  let sourceKind = inferKind(mimeType, absolutePath, detectionOptions);
9012
- if (sourceKind === "binary" && path12.extname(absolutePath).toLowerCase() === ".zip") {
11601
+ const lowerExt = path12.extname(absolutePath).toLowerCase();
11602
+ if ((lowerExt === ".html" || lowerExt === ".htm") && sourceKind === "code") {
11603
+ sourceKind = "html";
11604
+ }
11605
+ if (sourceKind === "binary" && lowerExt === ".zip") {
9013
11606
  const bytes = await fs11.readFile(absolutePath);
9014
11607
  if (isSlackExportArchive(bytes)) {
9015
11608
  sourceKind = "chat_export";
@@ -9213,9 +11806,55 @@ import { z as z7 } from "zod";
9213
11806
 
9214
11807
  // src/analysis.ts
9215
11808
  import path14 from "path";
9216
- import nlp from "compromise";
9217
- import { fromMarkdown } from "mdast-util-from-markdown";
11809
+ import nlp2 from "compromise";
9218
11810
  import { z as z2 } from "zod";
11811
+
11812
+ // src/tokenize.ts
11813
+ import nlp from "compromise";
11814
+ var CLOSED_CLASS_POS_SELECTOR = "#Determiner, #Preposition, #Conjunction, #Pronoun, #Auxiliary, #Copula";
11815
+ function splitTermToTokens(term, tokens) {
11816
+ for (const piece of term.split(/[^a-z0-9-]+/)) {
11817
+ const trimmed = piece.replace(/^-+|-+$/g, "");
11818
+ if (trimmed.length >= 2) {
11819
+ tokens.push(trimmed);
11820
+ }
11821
+ }
11822
+ }
11823
+ function tokenize(text) {
11824
+ const lower = text.toLowerCase();
11825
+ try {
11826
+ const terms = nlp(lower).terms().out("array");
11827
+ const tokens = [];
11828
+ for (const term of terms) {
11829
+ splitTermToTokens(term, tokens);
11830
+ }
11831
+ if (tokens.length > 0) {
11832
+ return tokens;
11833
+ }
11834
+ } catch {
11835
+ }
11836
+ return lower.match(/[a-z0-9][a-z0-9-]{1,}/g) ?? [];
11837
+ }
11838
+ function contentTokens(text, minLength = 4) {
11839
+ const lower = text.toLowerCase();
11840
+ const tokens = [];
11841
+ try {
11842
+ const contentDoc = nlp(lower).not(CLOSED_CLASS_POS_SELECTOR);
11843
+ const terms = contentDoc.terms().out("array");
11844
+ for (const term of terms) {
11845
+ splitTermToTokens(term, tokens);
11846
+ }
11847
+ } catch {
11848
+ }
11849
+ if (tokens.length === 0) {
11850
+ for (const piece of lower.match(/[a-z0-9][a-z0-9-]{1,}/g) ?? []) {
11851
+ tokens.push(piece);
11852
+ }
11853
+ }
11854
+ return tokens.filter((token) => token.length >= minLength);
11855
+ }
11856
+
11857
+ // src/analysis.ts
9219
11858
  var ANALYSIS_FORMAT_VERSION = 7;
9220
11859
  var sourceAnalysisSchema = z2.object({
9221
11860
  title: z2.string().min(1),
@@ -9234,46 +11873,6 @@ var sourceAnalysisSchema = z2.object({
9234
11873
  questions: z2.array(z2.string()).max(6).default([]),
9235
11874
  tags: z2.array(z2.string()).max(5).default([])
9236
11875
  });
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
11876
  var HEURISTIC_SECTION_SOURCE_KINDS = /* @__PURE__ */ new Map([
9278
11877
  ["transcript", "Transcript"],
9279
11878
  ["chat_export", "Messages"],
@@ -9282,10 +11881,7 @@ var HEURISTIC_SECTION_SOURCE_KINDS = /* @__PURE__ */ new Map([
9282
11881
  ]);
9283
11882
  function extractTopTerms(text, count) {
9284
11883
  const frequency = /* @__PURE__ */ new Map();
9285
- for (const token of text.toLowerCase().match(/[a-z][a-z0-9-]{3,}/g) ?? []) {
9286
- if (STOPWORDS.has(token)) {
9287
- continue;
9288
- }
11884
+ for (const token of contentTokens(text)) {
9289
11885
  frequency.set(token, (frequency.get(token) ?? 0) + 1);
9290
11886
  }
9291
11887
  return [...frequency.entries()].sort((left, right) => right[1] - left[1] || left[0].localeCompare(right[0])).slice(0, count).map(([token]) => token);
@@ -9293,7 +11889,7 @@ function extractTopTerms(text, count) {
9293
11889
  function extractEntities(text, count) {
9294
11890
  const candidates = [];
9295
11891
  try {
9296
- const doc = nlp(text);
11892
+ const doc = nlp2(text);
9297
11893
  const segments = [
9298
11894
  doc.match("#ProperNoun+").out("array"),
9299
11895
  doc.people().out("array"),
@@ -9311,10 +11907,6 @@ function extractEntities(text, count) {
9311
11907
  }
9312
11908
  } catch {
9313
11909
  }
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
11910
  return uniqueBy(candidates, (value) => value.toLowerCase()).slice(0, count);
9319
11911
  }
9320
11912
  function detectPolarity(text) {
@@ -9326,26 +11918,6 @@ function detectPolarity(text) {
9326
11918
  }
9327
11919
  return "neutral";
9328
11920
  }
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
11921
  function markdownNodesText(nodes) {
9350
11922
  return normalizeWhitespace(nodes.map((node) => markdownNodeText(node)).join("\n"));
9351
11923
  }
@@ -10620,7 +13192,7 @@ function filterGraphBySourceClass(graph, sourceClass) {
10620
13192
  }
10621
13193
 
10622
13194
  // src/graph-enrichment.ts
10623
- var STOPWORDS2 = /* @__PURE__ */ new Set([
13195
+ var STOPWORDS = /* @__PURE__ */ new Set([
10624
13196
  "about",
10625
13197
  "after",
10626
13198
  "also",
@@ -10684,7 +13256,7 @@ function addFeature(bucket, reason, value) {
10684
13256
  }
10685
13257
  function themeTokens(value) {
10686
13258
  return uniqueBy(
10687
- normalizeValue(value).split(/[^a-z0-9]+/i).filter((token) => token.length >= 4 && !STOPWORDS2.has(token)),
13259
+ normalizeValue(value).split(/[^a-z0-9]+/i).filter((token) => token.length >= 4 && !STOPWORDS.has(token)),
10688
13260
  (token) => token
10689
13261
  ).slice(0, 6);
10690
13262
  }
@@ -13319,8 +15891,7 @@ function getDatabaseSync() {
13319
15891
  return builtin.DatabaseSync;
13320
15892
  }
13321
15893
  function toFtsQuery(query) {
13322
- const tokens = query.toLowerCase().match(/[a-z0-9]{2,}/g)?.filter(Boolean) ?? [];
13323
- return tokens.join(" OR ");
15894
+ return tokenize(query).join(" OR ");
13324
15895
  }
13325
15896
  function normalizeKind(value) {
13326
15897
  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 +16320,7 @@ async function resolveImageGenerationProvider(rootDir) {
13749
16320
  if (!providerConfig) {
13750
16321
  throw new Error(`No provider configured with id "${preferredProviderId}" for task "imageProvider".`);
13751
16322
  }
13752
- const { createProvider: createProvider2 } = await import("./registry-NBLIJHZT.js");
16323
+ const { createProvider: createProvider2 } = await import("./registry-W6ZFRI73.js");
13753
16324
  return createProvider2(preferredProviderId, providerConfig, rootDir);
13754
16325
  }
13755
16326
  async function generateOutputArtifacts(rootDir, input) {
@@ -18016,7 +20587,7 @@ async function bootstrapDemo(rootDir, input) {
18016
20587
  }
18017
20588
 
18018
20589
  // src/mcp.ts
18019
- var SERVER_VERSION = "0.6.7";
20590
+ var SERVER_VERSION = "0.7.0";
18020
20591
  async function createMcpServer(rootDir) {
18021
20592
  const server = new McpServer({
18022
20593
  name: "swarmvault",
@@ -18028,10 +20599,10 @@ async function createMcpServer(rootDir) {
18028
20599
  {
18029
20600
  description: "Return the current SwarmVault workspace paths and high-level counts."
18030
20601
  },
18031
- async () => {
20602
+ safeHandler(async () => {
18032
20603
  const info = await getWorkspaceInfo(rootDir);
18033
20604
  return asToolText(info);
18034
- }
20605
+ })
18035
20606
  );
18036
20607
  server.registerTool(
18037
20608
  "search_pages",
@@ -18042,10 +20613,10 @@ async function createMcpServer(rootDir) {
18042
20613
  limit: z8.number().int().min(1).max(25).optional().describe("Maximum number of results")
18043
20614
  }
18044
20615
  },
18045
- async ({ query, limit }) => {
20616
+ safeHandler(async ({ query, limit }) => {
18046
20617
  const results = await searchVault(rootDir, query, limit ?? 5);
18047
20618
  return asToolText(results);
18048
- }
20619
+ })
18049
20620
  );
18050
20621
  server.registerTool(
18051
20622
  "read_page",
@@ -18055,13 +20626,13 @@ async function createMcpServer(rootDir) {
18055
20626
  path: z8.string().min(1).describe("Path relative to wiki/, for example sources/example.md")
18056
20627
  }
18057
20628
  },
18058
- async ({ path: relativePath }) => {
20629
+ safeHandler(async ({ path: relativePath }) => {
18059
20630
  const page = await readPage(rootDir, relativePath);
18060
20631
  if (!page) {
18061
20632
  return asToolError(`Page not found: ${relativePath}`);
18062
20633
  }
18063
20634
  return asToolText(page);
18064
- }
20635
+ })
18065
20636
  );
18066
20637
  server.registerTool(
18067
20638
  "list_sources",
@@ -18071,10 +20642,10 @@ async function createMcpServer(rootDir) {
18071
20642
  limit: z8.number().int().min(1).max(100).optional().describe("Maximum number of manifests to return")
18072
20643
  }
18073
20644
  },
18074
- async ({ limit }) => {
20645
+ safeHandler(async ({ limit }) => {
18075
20646
  const manifests = await listManifests(rootDir);
18076
20647
  return asToolText(limit ? manifests.slice(0, limit) : manifests);
18077
- }
20648
+ })
18078
20649
  );
18079
20650
  server.registerTool(
18080
20651
  "query_graph",
@@ -18086,22 +20657,22 @@ async function createMcpServer(rootDir) {
18086
20657
  budget: z8.number().int().min(3).max(50).optional().describe("Maximum nodes to summarize")
18087
20658
  }
18088
20659
  },
18089
- async ({ question, traversal, budget }) => {
20660
+ safeHandler(async ({ question, traversal, budget }) => {
18090
20661
  const result = await queryGraphVault(rootDir, question, {
18091
20662
  traversal,
18092
20663
  budget
18093
20664
  });
18094
20665
  return asToolText(result);
18095
- }
20666
+ })
18096
20667
  );
18097
20668
  server.registerTool(
18098
20669
  "graph_report",
18099
20670
  {
18100
20671
  description: "Return the machine-readable graph report and trust artifact."
18101
20672
  },
18102
- async () => {
20673
+ safeHandler(async () => {
18103
20674
  return asToolText(await readGraphReport(rootDir) ?? { error: "Graph report not found. Run `swarmvault compile` first." });
18104
- }
20675
+ })
18105
20676
  );
18106
20677
  server.registerTool(
18107
20678
  "get_node",
@@ -18111,9 +20682,9 @@ async function createMcpServer(rootDir) {
18111
20682
  target: z8.string().min(1).describe("Node or page label/id")
18112
20683
  }
18113
20684
  },
18114
- async ({ target }) => {
20685
+ safeHandler(async ({ target }) => {
18115
20686
  return asToolText(await explainGraphVault(rootDir, target));
18116
- }
20687
+ })
18117
20688
  );
18118
20689
  server.registerTool(
18119
20690
  "get_hyperedges",
@@ -18124,9 +20695,9 @@ async function createMcpServer(rootDir) {
18124
20695
  limit: z8.number().int().min(1).max(50).optional().describe("Maximum hyperedges to return")
18125
20696
  }
18126
20697
  },
18127
- async ({ target, limit }) => {
20698
+ safeHandler(async ({ target, limit }) => {
18128
20699
  return asToolText(await listGraphHyperedges(rootDir, target, limit ?? 25));
18129
- }
20700
+ })
18130
20701
  );
18131
20702
  server.registerTool(
18132
20703
  "get_neighbors",
@@ -18136,10 +20707,10 @@ async function createMcpServer(rootDir) {
18136
20707
  target: z8.string().min(1).describe("Node or page label/id")
18137
20708
  }
18138
20709
  },
18139
- async ({ target }) => {
20710
+ safeHandler(async ({ target }) => {
18140
20711
  const explanation = await explainGraphVault(rootDir, target);
18141
20712
  return asToolText(explanation.neighbors);
18142
- }
20713
+ })
18143
20714
  );
18144
20715
  server.registerTool(
18145
20716
  "shortest_path",
@@ -18150,9 +20721,9 @@ async function createMcpServer(rootDir) {
18150
20721
  to: z8.string().min(1).describe("End node/page label or id")
18151
20722
  }
18152
20723
  },
18153
- async ({ from, to }) => {
20724
+ safeHandler(async ({ from, to }) => {
18154
20725
  return asToolText(await pathGraphVault(rootDir, from, to));
18155
- }
20726
+ })
18156
20727
  );
18157
20728
  server.registerTool(
18158
20729
  "god_nodes",
@@ -18162,9 +20733,9 @@ async function createMcpServer(rootDir) {
18162
20733
  limit: z8.number().int().min(1).max(25).optional().describe("Maximum nodes to return")
18163
20734
  }
18164
20735
  },
18165
- async ({ limit }) => {
20736
+ safeHandler(async ({ limit }) => {
18166
20737
  return asToolText(await listGodNodes(rootDir, limit ?? 10));
18167
- }
20738
+ })
18168
20739
  );
18169
20740
  server.registerTool(
18170
20741
  "query_vault",
@@ -18176,14 +20747,14 @@ async function createMcpServer(rootDir) {
18176
20747
  format: z8.enum(["markdown", "report", "slides", "chart", "image"]).optional().describe("Output format")
18177
20748
  }
18178
20749
  },
18179
- async ({ question, save, format }) => {
20750
+ safeHandler(async ({ question, save, format }) => {
18180
20751
  const result = await queryVault(rootDir, {
18181
20752
  question,
18182
20753
  save: save ?? true,
18183
20754
  format
18184
20755
  });
18185
20756
  return asToolText(result);
18186
- }
20757
+ })
18187
20758
  );
18188
20759
  server.registerTool(
18189
20760
  "ingest_input",
@@ -18193,10 +20764,10 @@ async function createMcpServer(rootDir) {
18193
20764
  input: z8.string().min(1).describe("Local path or URL to ingest")
18194
20765
  }
18195
20766
  },
18196
- async ({ input }) => {
20767
+ safeHandler(async ({ input }) => {
18197
20768
  const result = await ingestInputDetailed(rootDir, input);
18198
20769
  return asToolText(result);
18199
- }
20770
+ })
18200
20771
  );
18201
20772
  server.registerTool(
18202
20773
  "compile_vault",
@@ -18206,20 +20777,20 @@ async function createMcpServer(rootDir) {
18206
20777
  approve: z8.boolean().optional().describe("Stage a review bundle without applying active page changes")
18207
20778
  }
18208
20779
  },
18209
- async ({ approve }) => {
20780
+ safeHandler(async ({ approve }) => {
18210
20781
  const result = await compileVault(rootDir, { approve: approve ?? false });
18211
20782
  return asToolText(result);
18212
- }
20783
+ })
18213
20784
  );
18214
20785
  server.registerTool(
18215
20786
  "lint_vault",
18216
20787
  {
18217
20788
  description: "Run anti-drift and vault health checks."
18218
20789
  },
18219
- async () => {
20790
+ safeHandler(async () => {
18220
20791
  const findings = await lintVault(rootDir);
18221
20792
  return asToolText(findings);
18222
- }
20793
+ })
18223
20794
  );
18224
20795
  server.registerResource(
18225
20796
  "swarmvault-config",
@@ -18390,6 +20961,17 @@ function asToolError(message) {
18390
20961
  ]
18391
20962
  };
18392
20963
  }
20964
+ function safeHandler(handler) {
20965
+ return async (args) => {
20966
+ try {
20967
+ return await handler(args);
20968
+ } catch (error) {
20969
+ const message = error instanceof Error ? error.message : String(error);
20970
+ console.error(`[swarmvault-mcp] tool handler failed: ${message}`);
20971
+ return asToolError(message);
20972
+ }
20973
+ };
20974
+ }
18393
20975
  function asTextResource(uri, text) {
18394
20976
  return {
18395
20977
  contents: [
@@ -18637,13 +21219,22 @@ async function serveSchedules(rootDir, pollMs = 3e4) {
18637
21219
  }
18638
21220
  running = true;
18639
21221
  try {
18640
- const schedules = await listSchedules(rootDir);
21222
+ let schedules = [];
21223
+ try {
21224
+ schedules = await listSchedules(rootDir);
21225
+ } catch (error) {
21226
+ console.error(`[swarmvault-schedule] failed to list schedules: ${error instanceof Error ? error.message : String(error)}`);
21227
+ }
18641
21228
  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
21229
  for (const schedule of due) {
18643
21230
  if (closed) {
18644
21231
  break;
18645
21232
  }
18646
- await runSchedule(rootDir, schedule.jobId);
21233
+ try {
21234
+ await runSchedule(rootDir, schedule.jobId);
21235
+ } catch (error) {
21236
+ console.error(`[swarmvault-schedule] job ${schedule.jobId} crashed: ${error instanceof Error ? error.message : String(error)}`);
21237
+ }
18647
21238
  }
18648
21239
  } finally {
18649
21240
  running = false;
@@ -20586,10 +23177,6 @@ var MAX_BACKOFF_MS = 3e4;
20586
23177
  var BACKOFF_THRESHOLD = 3;
20587
23178
  var CRITICAL_THRESHOLD = 10;
20588
23179
  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
23180
  function hasIgnoredRepoSegment(baseDir, targetPath) {
20594
23181
  const relativePath = path27.relative(baseDir, targetPath);
20595
23182
  if (!relativePath || relativePath.startsWith("..")) {
@@ -20759,11 +23346,11 @@ async function watchVault(rootDir, options = {}) {
20759
23346
  interval: 100,
20760
23347
  ignored: (targetPath) => {
20761
23348
  const absolutePath = path27.resolve(targetPath);
20762
- const primaryTarget = watchTargets.filter((watchTarget) => withinRoot3(watchTarget, absolutePath)).sort((left, right) => right.length - left.length)[0] ?? null;
23349
+ const primaryTarget = watchTargets.filter((watchTarget) => isPathWithin(watchTarget, absolutePath)).sort((left, right) => right.length - left.length)[0] ?? null;
20763
23350
  if (!primaryTarget) {
20764
23351
  return false;
20765
23352
  }
20766
- if (primaryTarget !== inboxWatchRoot && ignoredRoots.some((ignoreRoot) => withinRoot3(ignoreRoot, absolutePath))) {
23353
+ if (primaryTarget !== inboxWatchRoot && ignoredRoots.some((ignoreRoot) => isPathWithin(ignoreRoot, absolutePath))) {
20767
23354
  return true;
20768
23355
  }
20769
23356
  return hasIgnoredRepoSegment(primaryTarget, absolutePath);
@@ -20947,7 +23534,7 @@ async function watchVault(rootDir, options = {}) {
20947
23534
  }
20948
23535
  };
20949
23536
  const reasonForPath = (targetPath) => {
20950
- const baseDir = watchTargets.filter((watchTarget) => withinRoot3(watchTarget, path27.resolve(targetPath))).sort((left, right) => right.length - left.length)[0] ?? paths.inboxDir;
23537
+ const baseDir = watchTargets.filter((watchTarget) => isPathWithin(watchTarget, path27.resolve(targetPath))).sort((left, right) => right.length - left.length)[0] ?? paths.inboxDir;
20951
23538
  return path27.relative(baseDir, targetPath) || ".";
20952
23539
  };
20953
23540
  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)}`));