@openez-graph/cli 0.4.3 → 0.5.1

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/cli.cjs CHANGED
@@ -12147,7 +12147,7 @@ function createWorkspaceRepository(rootPath) {
12147
12147
  },
12148
12148
  // ── Graph Node Operations ──
12149
12149
  async upsertGraphNode(input) {
12150
- const existing = native.prepare("SELECT * FROM graph_nodes WHERE type = ? AND label = ?").get(input.type, input.label);
12150
+ const existing = input.type === "symbol" && input.refId ? native.prepare("SELECT * FROM graph_nodes WHERE type = ? AND label = ? AND ref_id = ?").get(input.type, input.label, input.refId) : native.prepare("SELECT * FROM graph_nodes WHERE type = ? AND label = ?").get(input.type, input.label);
12151
12151
  if (existing) {
12152
12152
  const nextMetadata = input.metadata ?? String(existing.metadata ?? "{}");
12153
12153
  const existingRefId = existing.ref_id ?? null;
@@ -200816,9 +200816,9 @@ ${newComment.split("\n").map((c) => ` * ${c}`).join("\n")}
200816
200816
  if (packageName === void 0) return void 0;
200817
200817
  switch (context.fixId) {
200818
200818
  case fixIdInstallTypesPackage: {
200819
- const pkg = getTypesPackageNameToInstall(packageName, context.host, diag2.code);
200820
- if (pkg) {
200821
- commands.push(getInstallCommand(diag2.file.fileName, pkg));
200819
+ const pkg2 = getTypesPackageNameToInstall(packageName, context.host, diag2.code);
200820
+ if (pkg2) {
200821
+ commands.push(getInstallCommand(diag2.file.fileName, pkg2));
200822
200822
  }
200823
200823
  break;
200824
200824
  }
@@ -302577,7 +302577,14 @@ function indexCode(content, filePath) {
302577
302577
  }
302578
302578
  return {
302579
302579
  chunks: chunks2,
302580
- importPaths: sourceFile.getImportDeclarations().map((declaration) => declaration.getModuleSpecifierValue()),
302580
+ importPaths: sourceFile.getImportDeclarations().flatMap((declaration) => {
302581
+ try {
302582
+ const value = declaration.getModuleSpecifierValue();
302583
+ return typeof value === "string" && value.length > 0 ? [value] : [];
302584
+ } catch {
302585
+ return [];
302586
+ }
302587
+ }),
302581
302588
  definedSymbols,
302582
302589
  calledIdentifiers: [...calledIdentifiers],
302583
302590
  callExpressions
@@ -302769,6 +302776,10 @@ var init_markdown = __esm({
302769
302776
  });
302770
302777
 
302771
302778
  // ../../packages/indexer/src/languages.ts
302779
+ function codeSearchText2(text2) {
302780
+ const identifiers = text2.match(/[A-Za-z_$][A-Za-z0-9_$]*/g) ?? [];
302781
+ return [...new Set(identifiers.flatMap((identifier) => identifier.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").replace(/_/g, " ").split(" ").filter((term) => term.length > 1)))].slice(0, 256).join(" ");
302782
+ }
302772
302783
  function inferDocumentKind(filePath) {
302773
302784
  const extension = import_node_path9.default.extname(filePath).toLowerCase();
302774
302785
  if (markdownExtensions.has(extension)) {
@@ -302782,6 +302793,75 @@ function inferDocumentKind(filePath) {
302782
302793
  }
302783
302794
  return { kind: "text", language: extension.slice(1) || null, extension };
302784
302795
  }
302796
+ function isEscaped(text2, index2) {
302797
+ let slashes = 0;
302798
+ while (text2[index2 - slashes - 1] === "\\") slashes++;
302799
+ return slashes % 2 === 1;
302800
+ }
302801
+ function stripNonCode(content, options = {}) {
302802
+ let result = "";
302803
+ let quote = "";
302804
+ let blockComment = false;
302805
+ let lineComment = false;
302806
+ for (let i = 0; i < content.length; i++) {
302807
+ const char = content[i];
302808
+ const next = content[i + 1];
302809
+ const triple = content.slice(i, i + 3);
302810
+ if (char === "\n") {
302811
+ result += "\n";
302812
+ lineComment = false;
302813
+ continue;
302814
+ }
302815
+ if (lineComment) {
302816
+ result += " ";
302817
+ continue;
302818
+ }
302819
+ if (blockComment) {
302820
+ result += " ";
302821
+ if (char === "*" && next === "/") {
302822
+ result += " ";
302823
+ blockComment = false;
302824
+ i++;
302825
+ }
302826
+ continue;
302827
+ }
302828
+ if (quote) {
302829
+ result += " ";
302830
+ if (quote.length === 3 && triple === quote) {
302831
+ result += " ";
302832
+ quote = "";
302833
+ i += 2;
302834
+ } else if (quote.length === 1 && char === quote && !isEscaped(content, i)) {
302835
+ quote = "";
302836
+ }
302837
+ continue;
302838
+ }
302839
+ if (options.hashComments && char === "#") {
302840
+ result += " ";
302841
+ lineComment = true;
302842
+ } else if (!options.hashComments && char === "/" && next === "/") {
302843
+ result += " ";
302844
+ lineComment = true;
302845
+ i++;
302846
+ } else if (!options.hashComments && char === "/" && next === "*") {
302847
+ result += " ";
302848
+ blockComment = true;
302849
+ i++;
302850
+ } else if (options.tripleStrings && (triple === '"""' || triple === "'''")) {
302851
+ result += " ";
302852
+ quote = triple;
302853
+ i += 2;
302854
+ } else if (options.rustLifetimes && char === "'" && /[A-Za-z_]/.test(next ?? "") && content[i + 2] !== "'") {
302855
+ result += char;
302856
+ } else if (char === '"' || char === "'" || options.backtickStrings && char === "`") {
302857
+ result += " ";
302858
+ quote = char;
302859
+ } else {
302860
+ result += char;
302861
+ }
302862
+ }
302863
+ return result;
302864
+ }
302785
302865
  function stripPythonAlias(value) {
302786
302866
  return value.trim().replace(/^\(+|\)+$/g, "").split(/\s+as\s+/i)[0]?.trim() ?? "";
302787
302867
  }
@@ -302806,84 +302886,160 @@ function normalizePythonCallName(value) {
302806
302886
  }
302807
302887
  function parsePython(content) {
302808
302888
  const lines = content.split("\n");
302889
+ const codeLines = stripNonCode(content, { hashComments: true, tripleStrings: true }).split("\n");
302809
302890
  const definedSymbols = [];
302810
302891
  const importPaths = [];
302811
302892
  const calledIdentifiers = /* @__PURE__ */ new Set();
302812
302893
  const callExpressions = [];
302813
302894
  const symbolRegex = /^(?:async\s+)?(?:def|class)\s+(\w+)/;
302814
- const moduleDocstring = /^"""/;
302815
302895
  const callRegex = /(\w+(?:\.\w+)*)\s*\(/g;
302816
- let inMultilineString = false;
302896
+ const decoratorRegex = /^@(\w+(?:\.\w+)*)/;
302897
+ let pendingDecorators = [];
302898
+ const symbolStack = [];
302817
302899
  for (let i = 0; i < lines.length; i++) {
302818
302900
  const line = lines[i];
302819
- if (inMultilineString) {
302820
- if (line.includes('"""') || line.includes("'''")) {
302821
- inMultilineString = false;
302901
+ while (symbolStack.length > 0 && i >= symbolStack[symbolStack.length - 1].endLine) {
302902
+ symbolStack.pop();
302903
+ }
302904
+ const trimmed = codeLines[i].trim();
302905
+ const decoratorMatch = decoratorRegex.exec(trimmed);
302906
+ if (decoratorMatch) {
302907
+ pendingDecorators.push({ name: decoratorMatch[1], lineIndex: i });
302908
+ const decoratorCalls = decoratorMatch[1];
302909
+ const decoratorCalledName = normalizePythonCallName(decoratorCalls);
302910
+ if (!PYTHON_CALL_IGNORES.has(decoratorCalledName)) {
302911
+ calledIdentifiers.add(decoratorCalledName);
302822
302912
  }
302823
302913
  continue;
302824
302914
  }
302825
- if (moduleDocstring.test(line.trim())) {
302826
- if (!line.trim().endsWith('"""') && !line.trim().endsWith("'''")) {
302827
- inMultilineString = true;
302828
- }
302829
- continue;
302830
- }
302831
- const trimmed = line.trim();
302832
302915
  const symbolMatch = symbolRegex.exec(trimmed);
302833
302916
  if (symbolMatch) {
302834
- const name = symbolMatch[1];
302917
+ const rawName = symbolMatch[1];
302835
302918
  const isAsync2 = trimmed.startsWith("async");
302836
302919
  const stripped = isAsync2 ? trimmed.slice(6) : trimmed;
302837
302920
  const symbolType2 = stripped.startsWith("def") ? "function" : "class";
302838
- const exported = !name.startsWith("_");
302839
- const startLine = i + 1;
302840
- const endLine = findBlockEnd(lines, i);
302841
- const content2 = lines.slice(i, endLine).join("\n");
302842
- definedSymbols.push({ name, symbolType: symbolType2, type: symbolType2, exported, startLine, endLine });
302843
- const bodyContent = lines.slice(i, endLine).join("\n");
302921
+ const exported = !rawName.startsWith("_");
302922
+ const endLine = findBlockEnd(codeLines, i);
302923
+ const parentName = symbolStack.length > 0 ? symbolStack[symbolStack.length - 1].name : null;
302924
+ const name = parentName ? `${parentName}::${rawName}` : rawName;
302925
+ const decoratorNames = pendingDecorators.map((d) => d.name);
302926
+ const decoratorStartLine = pendingDecorators.length > 0 ? pendingDecorators[0].lineIndex + 1 : i + 1;
302927
+ const startLine = decoratorStartLine;
302928
+ const content2 = lines.slice(startLine - 1, endLine).join("\n");
302929
+ definedSymbols.push({ name, symbolType: symbolType2, type: symbolType2, exported, startLine, endLine, decorators: decoratorNames });
302930
+ const bodyContent = codeLines.slice(i, endLine).join("\n");
302844
302931
  let callMatch;
302845
302932
  const localCallRegex = new RegExp(callRegex);
302846
302933
  while ((callMatch = localCallRegex.exec(bodyContent)) !== null) {
302847
302934
  const rawCalledName = callMatch[1];
302848
302935
  const calledName = normalizePythonCallName(rawCalledName);
302849
- if (!PYTHON_CALL_IGNORES.has(rawCalledName) && !PYTHON_CALL_IGNORES.has(calledName) && calledName !== name) {
302936
+ if (!PYTHON_CALL_IGNORES.has(rawCalledName) && !PYTHON_CALL_IGNORES.has(calledName) && calledName !== rawName && calledName !== name) {
302850
302937
  calledIdentifiers.add(calledName);
302851
302938
  callExpressions.push({ callerName: name, calleeName: calledName });
302852
302939
  }
302853
302940
  }
302941
+ for (const dec of pendingDecorators) {
302942
+ const decCalledName = normalizePythonCallName(dec.name);
302943
+ if (!PYTHON_CALL_IGNORES.has(decCalledName) && decCalledName !== name) {
302944
+ calledIdentifiers.add(decCalledName);
302945
+ callExpressions.push({ callerName: name, calleeName: decCalledName });
302946
+ }
302947
+ const decLine = codeLines[dec.lineIndex];
302948
+ let decCallMatch;
302949
+ const decCallRegex = new RegExp(callRegex);
302950
+ while ((decCallMatch = decCallRegex.exec(decLine)) !== null) {
302951
+ const rawCalledName = decCallMatch[1];
302952
+ const calledName = normalizePythonCallName(rawCalledName);
302953
+ if (!PYTHON_CALL_IGNORES.has(rawCalledName) && !PYTHON_CALL_IGNORES.has(calledName) && calledName !== name && calledName !== decCalledName) {
302954
+ calledIdentifiers.add(calledName);
302955
+ callExpressions.push({ callerName: name, calleeName: calledName });
302956
+ }
302957
+ }
302958
+ }
302959
+ pendingDecorators = [];
302960
+ symbolStack.push({ name, endLine });
302854
302961
  }
302855
302962
  importPaths.push(...parsePythonImportLine(line));
302856
302963
  }
302857
302964
  const chunks2 = createSymbolChunks(definedSymbols, lines, "python");
302858
302965
  if (chunks2.length === 0) {
302859
- return { ...makeFallbackChunks(content, lines), callExpressions: [] };
302966
+ return { ...makeFallbackChunks(content, lines), importPaths: [...new Set(importPaths)] };
302860
302967
  }
302861
302968
  return { chunks: chunks2, importPaths: [...new Set(importPaths)], definedSymbols, calledIdentifiers: [...calledIdentifiers], callExpressions };
302862
302969
  }
302970
+ function normalizeGoCallName(value) {
302971
+ const parts = value.split(".").filter(Boolean);
302972
+ return parts[parts.length - 1] ?? value;
302973
+ }
302974
+ function parseGoImports(lines) {
302975
+ const imports = [];
302976
+ const singleImportRegex = /^import\s+(?:[.\w]+\s+)?"([^"]+)"/;
302977
+ const groupedImportPathRegex = /^\s*(?:[.\w]+\s+)?"([^"]+)"/;
302978
+ for (let i = 0; i < lines.length; i++) {
302979
+ const trimmed = lines[i].trim();
302980
+ if (trimmed === "import (" || /^import\s*\(/.test(trimmed)) {
302981
+ for (let j = i + 1; j < lines.length; j++) {
302982
+ const innerTrimmed = lines[j].trim();
302983
+ if (innerTrimmed === ")") break;
302984
+ const pathMatch = groupedImportPathRegex.exec(lines[j]);
302985
+ if (pathMatch) {
302986
+ imports.push(pathMatch[1]);
302987
+ }
302988
+ }
302989
+ continue;
302990
+ }
302991
+ const singleMatch = singleImportRegex.exec(trimmed);
302992
+ if (singleMatch) {
302993
+ imports.push(singleMatch[1]);
302994
+ }
302995
+ }
302996
+ return imports;
302997
+ }
302863
302998
  function parseGo(content) {
302864
302999
  const lines = content.split("\n");
303000
+ const codeLines = stripNonCode(content, { backtickStrings: true }).split("\n");
302865
303001
  const definedSymbols = [];
302866
- const importPaths = [];
302867
- const funcRegex = /^func\s+(?:\([^)]*\)\s+)?(\w+)/;
303002
+ const calledIdentifiers = /* @__PURE__ */ new Set();
303003
+ const callExpressions = [];
303004
+ const funcRegex = /^func\s+(?:\(([^)]*)\)\s+)?(\w+)/;
302868
303005
  const typeRegex = /^type\s+(\w+)\s+(?:struct|interface|func|map|chan|\w+)/;
302869
303006
  const constVarRegex = /^(?:const|var)\s+(\w+)/;
302870
- const importRegex = /^import\s+(?:"(\S+)"|\(|(\S+))/;
303007
+ const callRegex = /(\w+(?:\.\w+)*)\s*\(/g;
303008
+ const importPaths = parseGoImports(lines);
302871
303009
  for (let i = 0; i < lines.length; i++) {
302872
- const trimmed = lines[i].trim();
303010
+ const trimmed = codeLines[i].trim();
302873
303011
  const funcMatch = funcRegex.exec(trimmed);
302874
303012
  if (funcMatch) {
302875
- const name = funcMatch[1];
303013
+ const receiver = funcMatch[1]?.trim();
303014
+ const name = funcMatch[2];
303015
+ const receiverParts = receiver?.match(/[A-Za-z_]\w*/g) ?? [];
303016
+ const receiverName = receiverParts[0];
303017
+ const receiverType = receiverParts[receiverParts.length - 1];
302876
303018
  const exported = name[0] >= "A" && name[0] <= "Z";
302877
303019
  const startLine = i + 1;
302878
- const endLine = findBraceBlockEnd(lines, i);
303020
+ const endLine = findBraceBlockEnd(codeLines, i);
303021
+ const symbolName = receiverType ? `${receiverType}::${name}` : name;
302879
303022
  definedSymbols.push({
302880
- name,
303023
+ name: symbolName,
302881
303024
  symbolType: "function",
302882
303025
  type: "function",
302883
303026
  exported,
302884
303027
  startLine,
302885
- endLine
303028
+ endLine,
303029
+ ...receiver ? { receiver: receiver.trim() } : {}
302886
303030
  });
303031
+ const bodyContent = codeLines.slice(i, endLine).join("\n");
303032
+ let callMatch;
303033
+ const localCallRegex = new RegExp(callRegex);
303034
+ while ((callMatch = localCallRegex.exec(bodyContent)) !== null) {
303035
+ const rawCalledName = callMatch[1];
303036
+ const calledName = normalizeGoCallName(rawCalledName);
303037
+ const calleeName = receiverName && rawCalledName.startsWith(`${receiverName}.`) && receiverType ? `${receiverType}::${calledName}` : calledName;
303038
+ if (!GO_CALL_IGNORES.has(rawCalledName) && !GO_CALL_IGNORES.has(calledName) && calleeName !== symbolName) {
303039
+ calledIdentifiers.add(calleeName);
303040
+ callExpressions.push({ callerName: symbolName, calleeName });
303041
+ }
303042
+ }
302887
303043
  continue;
302888
303044
  }
302889
303045
  const typeMatch = typeRegex.exec(trimmed);
@@ -302891,7 +303047,7 @@ function parseGo(content) {
302891
303047
  const name = typeMatch[1];
302892
303048
  const exported = name[0] >= "A" && name[0] <= "Z";
302893
303049
  const startLine = i + 1;
302894
- const endLine = findBraceBlockEnd(lines, i);
303050
+ const endLine = findBraceBlockEnd(codeLines, i);
302895
303051
  definedSymbols.push({
302896
303052
  name,
302897
303053
  symbolType: "type",
@@ -302915,40 +303071,115 @@ function parseGo(content) {
302915
303071
  endLine: i + 1
302916
303072
  });
302917
303073
  }
302918
- const importMatch = importRegex.exec(trimmed);
302919
- if (importMatch) {
302920
- const pkg = importMatch[1] || importMatch[2];
302921
- importPaths.push(pkg);
302922
- }
302923
303074
  }
302924
303075
  const chunks2 = createSymbolChunks(definedSymbols, lines, "go");
302925
303076
  if (chunks2.length === 0) {
302926
- return makeFallbackChunks(content, lines);
303077
+ return { ...makeFallbackChunks(content, lines), importPaths };
302927
303078
  }
302928
- return { chunks: chunks2, importPaths, definedSymbols, calledIdentifiers: [], callExpressions: [] };
303079
+ return { chunks: chunks2, importPaths, definedSymbols, calledIdentifiers: [...calledIdentifiers], callExpressions };
303080
+ }
303081
+ function normalizeRustCallName(value) {
303082
+ const parts = value.split("::").filter(Boolean);
303083
+ const last = parts[parts.length - 1] ?? value;
303084
+ const dotParts = last.split(".");
303085
+ return dotParts[dotParts.length - 1] ?? last;
302929
303086
  }
302930
303087
  function parseRust(content) {
302931
303088
  const lines = content.split("\n");
303089
+ const codeLines = stripNonCode(content, { rustLifetimes: true }).split("\n");
302932
303090
  const definedSymbols = [];
302933
303091
  const importPaths = [];
302934
- const fnRegex = /^(?:pub\s+)?(?:unsafe\s+)?fn\s+(\w+)/;
303092
+ const calledIdentifiers = /* @__PURE__ */ new Set();
303093
+ const callExpressions = [];
303094
+ const fnRegex = /^(?:pub\s+)?(?:unsafe\s+)?(?:async\s+)?fn\s+(\w+)/;
302935
303095
  const structRegex = /^(?:pub\s+)?struct\s+(\w+)/;
302936
303096
  const enumRegex = /^(?:pub\s+)?enum\s+(\w+)/;
302937
303097
  const traitRegex = /^(?:pub\s+)?trait\s+(\w+)/;
302938
- const implRegex = /^(?:pub\s+)?impl\s+/;
303098
+ const implRegex = /^(?:pub\s+)?impl\s+(?:<[^>]+>\s+)?([\w<>:,\s]+?)\s*(?:\{|for\s+)/;
303099
+ const implForRegex = /^(?:pub\s+)?impl\s+(?:<[^>]+>\s+)?(\w+)\s+for\s+([\w]+(?:<[^>]+>)?)/;
302939
303100
  const typeRegex = /^(?:pub\s+)?type\s+(\w+)/;
302940
303101
  const constRegex = /^(?:pub\s+)?(?:const|static)\s+(\w+)/;
302941
303102
  const modRegex = /^(?:pub\s+)?mod\s+(\w+)/;
302942
- const useRegex = /^use\s+(\S+)/;
303103
+ const useRegex = /^(?:pub(?:\([^)]*\))?\s+)?use\s+(.+?);?$/;
303104
+ const callRegex = /(\w+(?:::\w+)*(?:\.\w+)*)\s*\(/g;
303105
+ let implContext = null;
303106
+ let implBraceDepth = 0;
303107
+ let traitContext = null;
303108
+ let traitBraceDepth = 0;
302943
303109
  for (let i = 0; i < lines.length; i++) {
302944
- const trimmed = lines[i].trim();
303110
+ const trimmed = codeLines[i].trim();
303111
+ if (implContext) {
303112
+ implBraceDepth += countBraces(codeLines[i]);
303113
+ if (implBraceDepth <= 0) {
303114
+ implContext = null;
303115
+ implBraceDepth = 0;
303116
+ continue;
303117
+ }
303118
+ }
303119
+ if (traitContext) {
303120
+ traitBraceDepth += countBraces(codeLines[i]);
303121
+ if (traitBraceDepth <= 0) {
303122
+ traitContext = null;
303123
+ traitBraceDepth = 0;
303124
+ continue;
303125
+ }
303126
+ }
303127
+ const implForMatch = implForRegex.exec(trimmed);
303128
+ if (implForMatch) {
303129
+ const traitName = implForMatch[1];
303130
+ const typeName = implForMatch[2].trim();
303131
+ const startLine = i + 1;
303132
+ const endLine = findBraceBlockEnd(codeLines, i);
303133
+ definedSymbols.push({
303134
+ name: `impl ${traitName} for ${typeName}`,
303135
+ symbolType: "impl",
303136
+ type: "impl",
303137
+ exported: false,
303138
+ startLine,
303139
+ endLine
303140
+ });
303141
+ implContext = typeName;
303142
+ implBraceDepth = countBraces(codeLines[i]);
303143
+ if (implBraceDepth <= 0) implContext = null;
303144
+ continue;
303145
+ }
303146
+ const implMatch = implRegex.exec(trimmed);
303147
+ if (implMatch && !implForMatch) {
303148
+ const typeName = implMatch[1].trim();
303149
+ const startLine = i + 1;
303150
+ const endLine = findBraceBlockEnd(codeLines, i);
303151
+ definedSymbols.push({
303152
+ name: `impl ${typeName}`,
303153
+ symbolType: "impl",
303154
+ type: "impl",
303155
+ exported: false,
303156
+ startLine,
303157
+ endLine
303158
+ });
303159
+ implContext = typeName;
303160
+ implBraceDepth = countBraces(codeLines[i]);
303161
+ if (implBraceDepth <= 0) implContext = null;
303162
+ continue;
303163
+ }
302945
303164
  const fnMatch = fnRegex.exec(trimmed);
302946
303165
  if (fnMatch) {
302947
303166
  const name = fnMatch[1];
302948
303167
  const exported = trimmed.startsWith("pub");
302949
303168
  const startLine = i + 1;
302950
- const endLine = findBraceBlockEnd(lines, i);
302951
- definedSymbols.push({ name, symbolType: "function", type: "function", exported, startLine, endLine });
303169
+ const endLine = trimmed.endsWith(";") ? i + 1 : findBraceBlockEnd(codeLines, i);
303170
+ const symbolName = implContext ? `${implContext}::${name}` : traitContext ? `${traitContext}::${name}` : name;
303171
+ definedSymbols.push({ name: symbolName, symbolType: "function", type: "function", exported, startLine, endLine });
303172
+ const bodyContent = codeLines.slice(i, endLine).join("\n");
303173
+ let callMatch;
303174
+ const localCallRegex = new RegExp(callRegex);
303175
+ while ((callMatch = localCallRegex.exec(bodyContent)) !== null) {
303176
+ const rawCalledName = callMatch[1];
303177
+ const calledName = normalizeRustCallName(rawCalledName);
303178
+ if (!RUST_CALL_IGNORES.has(rawCalledName) && !RUST_CALL_IGNORES.has(calledName) && calledName !== name && calledName !== symbolName) {
303179
+ calledIdentifiers.add(calledName);
303180
+ callExpressions.push({ callerName: symbolName, calleeName: calledName });
303181
+ }
303182
+ }
302952
303183
  continue;
302953
303184
  }
302954
303185
  const structMatch = structRegex.exec(trimmed);
@@ -302956,7 +303187,7 @@ function parseRust(content) {
302956
303187
  const name = structMatch[1];
302957
303188
  const exported = trimmed.startsWith("pub");
302958
303189
  const startLine = i + 1;
302959
- const endLine = findBraceBlockEnd(lines, i);
303190
+ const endLine = findBraceBlockEnd(codeLines, i);
302960
303191
  definedSymbols.push({ name, symbolType: "struct", type: "struct", exported, startLine, endLine });
302961
303192
  continue;
302962
303193
  }
@@ -302965,17 +303196,20 @@ function parseRust(content) {
302965
303196
  const name = enumMatch[1];
302966
303197
  const exported = trimmed.startsWith("pub");
302967
303198
  const startLine = i + 1;
302968
- const endLine = findBraceBlockEnd(lines, i);
303199
+ const endLine = findBraceBlockEnd(codeLines, i);
302969
303200
  definedSymbols.push({ name, symbolType: "enum", type: "enum", exported, startLine, endLine });
302970
303201
  continue;
302971
303202
  }
302972
303203
  const traitMatch = traitRegex.exec(trimmed);
302973
- if (traitMatch && !implRegex.test(trimmed)) {
303204
+ if (traitMatch && !implRegex.test(trimmed) && !implForRegex.test(trimmed)) {
302974
303205
  const name = traitMatch[1];
302975
303206
  const exported = trimmed.startsWith("pub");
302976
303207
  const startLine = i + 1;
302977
- const endLine = findBraceBlockEnd(lines, i);
303208
+ const endLine = findBraceBlockEnd(codeLines, i);
302978
303209
  definedSymbols.push({ name, symbolType: "trait", type: "trait", exported, startLine, endLine });
303210
+ traitContext = name;
303211
+ traitBraceDepth = countBraces(codeLines[i]);
303212
+ if (traitBraceDepth <= 0) traitContext = null;
302979
303213
  continue;
302980
303214
  }
302981
303215
  const typeMatch = typeRegex.exec(trimmed);
@@ -303003,7 +303237,7 @@ function parseRust(content) {
303003
303237
  });
303004
303238
  }
303005
303239
  const modMatch = modRegex.exec(trimmed);
303006
- if (modMatch && !lines[i + 1]?.trim().startsWith(";")) {
303240
+ if (modMatch) {
303007
303241
  const name = modMatch[1];
303008
303242
  definedSymbols.push({
303009
303243
  name,
@@ -303011,19 +303245,19 @@ function parseRust(content) {
303011
303245
  type: "module",
303012
303246
  exported: trimmed.startsWith("pub"),
303013
303247
  startLine: i + 1,
303014
- endLine: findBraceBlockEnd(lines, i)
303248
+ endLine: trimmed.endsWith(";") ? i + 1 : findBraceBlockEnd(codeLines, i)
303015
303249
  });
303016
303250
  }
303017
303251
  const useMatch = useRegex.exec(trimmed);
303018
303252
  if (useMatch) {
303019
- importPaths.push(useMatch[1]);
303253
+ importPaths.push(useMatch[1].replace(/;$/, ""));
303020
303254
  }
303021
303255
  }
303022
303256
  const chunks2 = createSymbolChunks(definedSymbols, lines, "rust");
303023
303257
  if (chunks2.length === 0) {
303024
- return makeFallbackChunks(content, lines);
303258
+ return { ...makeFallbackChunks(content, lines), importPaths };
303025
303259
  }
303026
- return { chunks: chunks2, importPaths, definedSymbols, calledIdentifiers: [], callExpressions: [] };
303260
+ return { chunks: chunks2, importPaths, definedSymbols, calledIdentifiers: [...calledIdentifiers], callExpressions };
303027
303261
  }
303028
303262
  function indexConfig(content, language) {
303029
303263
  switch (language) {
@@ -303043,6 +303277,7 @@ function parseYamlConfig(content) {
303043
303277
  let currentSection = [];
303044
303278
  let currentKey = "root";
303045
303279
  let sectionStartLine = 1;
303280
+ let sectionIndent = 0;
303046
303281
  const flush = (endLine) => {
303047
303282
  const text2 = currentSection.join("\n").trim();
303048
303283
  if (!text2) return;
@@ -303062,14 +303297,31 @@ function parseYamlConfig(content) {
303062
303297
  };
303063
303298
  for (let i = 0; i < lines.length; i++) {
303064
303299
  const line = lines[i];
303065
- const topLevelMatch = /^(\w[\w\s.-]*?):/.exec(line);
303066
- if (topLevelMatch && (line.trim() === line || line.startsWith(topLevelMatch[1]))) {
303300
+ if (line.trim() === "" || line.trim().startsWith("#")) {
303301
+ currentSection.push(line);
303302
+ continue;
303303
+ }
303304
+ const indent = line.search(/\S/);
303305
+ const keyMatch = /^(\s*)([\w][\w\s.-]*?):(\s|$)/.exec(line);
303306
+ const bareListItemMatch = /^(\s*)-\s/.exec(line);
303307
+ const isTopLevel = indent === 0;
303308
+ const isListSection = bareListItemMatch && indent < sectionIndent;
303309
+ if (keyMatch && isTopLevel || isListSection) {
303067
303310
  if (currentSection.length > 0) {
303068
303311
  flush(i);
303069
303312
  }
303070
303313
  currentSection = [line];
303071
- currentKey = topLevelMatch[1].trim();
303314
+ currentKey = keyMatch ? keyMatch[2].trim() : `list-item-${i + 1}`;
303072
303315
  sectionStartLine = i + 1;
303316
+ sectionIndent = indent;
303317
+ } else if (keyMatch && indent < sectionIndent) {
303318
+ if (currentSection.length > 0) {
303319
+ flush(i);
303320
+ }
303321
+ currentSection = [line];
303322
+ currentKey = keyMatch[2].trim();
303323
+ sectionStartLine = i + 1;
303324
+ sectionIndent = indent;
303073
303325
  } else {
303074
303326
  currentSection.push(line);
303075
303327
  }
@@ -303114,6 +303366,7 @@ function parseTomlConfig(content) {
303114
303366
  let currentSection = [];
303115
303367
  let currentKey = "root";
303116
303368
  let sectionStartLine = 1;
303369
+ let isArrayTable = false;
303117
303370
  const flush = (endLine) => {
303118
303371
  const text2 = currentSection.join("\n").trim();
303119
303372
  if (!text2) return;
@@ -303126,6 +303379,7 @@ function parseTomlConfig(content) {
303126
303379
  kind: "config",
303127
303380
  language: "toml",
303128
303381
  section: currentKey,
303382
+ arrayTable: isArrayTable,
303129
303383
  startLine: sectionStartLine,
303130
303384
  endLine
303131
303385
  }
@@ -303133,13 +303387,28 @@ function parseTomlConfig(content) {
303133
303387
  };
303134
303388
  for (let i = 0; i < lines.length; i++) {
303135
303389
  const line = lines[i];
303136
- const sectionMatch = /^\[([^\]]+)\]/.exec(line.trim());
303137
- if (sectionMatch) {
303390
+ const trimmed = line.trim();
303391
+ if (trimmed === "" || trimmed.startsWith("#")) {
303392
+ currentSection.push(line);
303393
+ continue;
303394
+ }
303395
+ const arrayTableMatch = /^\[\[([^\]]+)\]\]/.exec(trimmed);
303396
+ const tableMatch = /^\[([^\]]+)\]/.exec(trimmed);
303397
+ if (arrayTableMatch) {
303138
303398
  if (currentSection.length > 0) {
303139
303399
  flush(i);
303140
303400
  }
303141
303401
  currentSection = [line];
303142
- currentKey = sectionMatch[1];
303402
+ currentKey = arrayTableMatch[1];
303403
+ isArrayTable = true;
303404
+ sectionStartLine = i + 1;
303405
+ } else if (tableMatch) {
303406
+ if (currentSection.length > 0) {
303407
+ flush(i);
303408
+ }
303409
+ currentSection = [line];
303410
+ currentKey = tableMatch[1];
303411
+ isArrayTable = false;
303143
303412
  sectionStartLine = i + 1;
303144
303413
  } else {
303145
303414
  currentSection.push(line);
@@ -303198,6 +303467,14 @@ function findBraceBlockEnd(lines, startIndex) {
303198
303467
  }
303199
303468
  return lines.length;
303200
303469
  }
303470
+ function countBraces(line) {
303471
+ let count = 0;
303472
+ for (const char of line) {
303473
+ if (char === "{") count++;
303474
+ else if (char === "}") count--;
303475
+ }
303476
+ return count;
303477
+ }
303201
303478
  function findSemicolonEnd(lines, startIndex) {
303202
303479
  for (let i = startIndex; i < lines.length; i++) {
303203
303480
  if (lines[i].trim().endsWith(";")) {
@@ -303218,12 +303495,14 @@ function createSymbolChunks(symbols, allLines, language) {
303218
303495
  symbolType: symbol.symbolType,
303219
303496
  metadata: {
303220
303497
  kind: "code",
303498
+ searchText: codeSearchText2(content),
303221
303499
  language,
303222
303500
  symbolName: symbol.name,
303223
303501
  symbolType: symbol.symbolType,
303224
303502
  exported: symbol.exported,
303225
303503
  startLine: symbol.startLine,
303226
- endLine: symbol.endLine
303504
+ endLine: symbol.endLine,
303505
+ ...symbol.decorators && symbol.decorators.length > 0 ? { decorators: symbol.decorators } : {}
303227
303506
  }
303228
303507
  };
303229
303508
  });
@@ -303239,6 +303518,7 @@ function makeFallbackChunks(content, lines) {
303239
303518
  contentHash: hashContent(slice),
303240
303519
  metadata: {
303241
303520
  kind: "code",
303521
+ searchText: codeSearchText2(slice),
303242
303522
  fallback: true,
303243
303523
  startLine: index2 + 1,
303244
303524
  endLine: Math.min(index2 + 80, lines.length)
@@ -303247,7 +303527,7 @@ function makeFallbackChunks(content, lines) {
303247
303527
  }
303248
303528
  return { chunks: chunks2, importPaths: [], definedSymbols: [], calledIdentifiers: [], callExpressions: [] };
303249
303529
  }
303250
- var import_node_path9, codeExtensions, configExtensions, markdownExtensions, PYTHON_CALL_IGNORES;
303530
+ var import_node_path9, codeExtensions, configExtensions, markdownExtensions, PYTHON_CALL_IGNORES, GO_CALL_IGNORES, RUST_CALL_IGNORES;
303251
303531
  var init_languages = __esm({
303252
303532
  "../../packages/indexer/src/languages.ts"() {
303253
303533
  "use strict";
@@ -303298,6 +303578,53 @@ var init_languages = __esm({
303298
303578
  "self",
303299
303579
  "cls"
303300
303580
  ]);
303581
+ GO_CALL_IGNORES = /* @__PURE__ */ new Set([
303582
+ "if",
303583
+ "for",
303584
+ "switch",
303585
+ "select",
303586
+ "case",
303587
+ "go",
303588
+ "defer",
303589
+ "return",
303590
+ "make",
303591
+ "len",
303592
+ "cap",
303593
+ "append",
303594
+ "copy",
303595
+ "delete",
303596
+ "panic",
303597
+ "recover",
303598
+ "new",
303599
+ "print",
303600
+ "println",
303601
+ "close",
303602
+ "complex",
303603
+ "real",
303604
+ "imag"
303605
+ ]);
303606
+ RUST_CALL_IGNORES = /* @__PURE__ */ new Set([
303607
+ "if",
303608
+ "while",
303609
+ "for",
303610
+ "loop",
303611
+ "match",
303612
+ "return",
303613
+ "let",
303614
+ "as",
303615
+ "in",
303616
+ "println",
303617
+ "print",
303618
+ "eprintln",
303619
+ "eprint",
303620
+ "format",
303621
+ "vec",
303622
+ "Box",
303623
+ "Some",
303624
+ "None",
303625
+ "Ok",
303626
+ "Err"
303627
+ ]);
303301
303628
  }
303302
303629
  });
303303
303630
 
@@ -303702,7 +304029,7 @@ async function indexWorkspace(input) {
303702
304029
  let filesUpdated = 0;
303703
304030
  let chunksWritten = 0;
303704
304031
  let embeddingsWritten = 0;
303705
- const symbolNodeIdsByName = /* @__PURE__ */ new Map();
304032
+ const symbolNodeIdsByFileAndName = /* @__PURE__ */ new Map();
303706
304033
  const pendingCallEdges = [];
303707
304034
  try {
303708
304035
  await reportProgress(
@@ -303796,7 +304123,8 @@ async function indexWorkspace(input) {
303796
304123
  });
303797
304124
  const symbolName = indexed.chunks[ci].symbolName;
303798
304125
  if (symbolName) {
303799
- const symbolNodeId = await repo.upsertGraphNode({
304126
+ const fileSymbolKey = `${file.relativePath}\0${symbolName}`;
304127
+ const symbolNodeId = symbolNodeIdsByFileAndName.get(fileSymbolKey) ?? await repo.upsertGraphNode({
303800
304128
  type: "symbol",
303801
304129
  label: symbolName,
303802
304130
  refId: chunkId,
@@ -303815,10 +304143,11 @@ async function indexWorkspace(input) {
303815
304143
  toNodeId: chunkNodeId,
303816
304144
  type: "represented_by"
303817
304145
  });
303818
- symbolNodeIdsByName.set(symbolName, symbolNodeId);
304146
+ symbolNodeIdsByFileAndName.set(fileSymbolKey, symbolNodeId);
303819
304147
  }
303820
304148
  }
303821
304149
  for (const importPath of indexed.importPaths) {
304150
+ if (typeof importPath !== "string" || importPath.length === 0) continue;
303822
304151
  const resolvedImportPath = workspaceFileResolver?.resolveImport(file.relativePath, importPath, indexed.language ?? void 0);
303823
304152
  if (!resolvedImportPath) continue;
303824
304153
  const targetNodeId = await repo.upsertGraphNode({
@@ -303845,7 +304174,7 @@ async function indexWorkspace(input) {
303845
304174
  type: "mentions"
303846
304175
  });
303847
304176
  }
303848
- pendingCallEdges.push(...indexed.callExpressions);
304177
+ pendingCallEdges.push(...indexed.callExpressions.map((call) => ({ ...call, filePath: file.relativePath })));
303849
304178
  const chunkRows = chunkIds.map((id, i) => ({
303850
304179
  id,
303851
304180
  content: indexed.chunks[i].content,
@@ -303858,8 +304187,13 @@ async function indexWorkspace(input) {
303858
304187
  }
303859
304188
  const insertedCallEdges = /* @__PURE__ */ new Set();
303860
304189
  for (const callExpression of pendingCallEdges) {
303861
- const callerNodeId = symbolNodeIdsByName.get(callExpression.callerName) ?? (await repo.findGraphNode("symbol", callExpression.callerName))?.id;
303862
- const calleeNodeId = symbolNodeIdsByName.get(callExpression.calleeName) ?? (await repo.findGraphNode("symbol", callExpression.calleeName))?.id;
304190
+ const callerNodeId = symbolNodeIdsByFileAndName.get(`${callExpression.filePath}\0${callExpression.callerName}`);
304191
+ const sameFileCallee = symbolNodeIdsByFileAndName.get(`${callExpression.filePath}\0${callExpression.calleeName}`);
304192
+ const globalCallees = sameFileCallee ? [] : await repo.queryRaw(
304193
+ "SELECT id FROM graph_nodes WHERE type = ? AND label = ? LIMIT 2",
304194
+ ["symbol", callExpression.calleeName]
304195
+ );
304196
+ const calleeNodeId = sameFileCallee ?? (globalCallees.length === 1 ? String(globalCallees[0].id) : void 0);
303863
304197
  if (!callerNodeId || !calleeNodeId || callerNodeId === calleeNodeId) continue;
303864
304198
  const edgeKey = `${callerNodeId}:${calleeNodeId}:calls`;
303865
304199
  if (insertedCallEdges.has(edgeKey)) continue;
@@ -319493,6 +319827,7 @@ function getRecentGraphRuns(rootPath, limit2 = 5) {
319493
319827
  function getWorkspaceGraphOptimized(rootPath, maxNodes, maxEdges) {
319494
319828
  const db = getWorkspaceDb2(rootPath);
319495
319829
  const countStmt = db.prepare("SELECT COUNT(*) AS count FROM graph_nodes");
319830
+ const edgeCountStmt = db.prepare("SELECT COUNT(*) AS count FROM graph_edges");
319496
319831
  const nodesStmt = db.prepare(`
319497
319832
  SELECT * FROM graph_nodes
319498
319833
  ORDER BY
@@ -319500,12 +319835,31 @@ function getWorkspaceGraphOptimized(rootPath, maxNodes, maxEdges) {
319500
319835
  created_at DESC
319501
319836
  LIMIT ?
319502
319837
  `);
319503
- const edgesStmt = db.prepare("SELECT * FROM graph_edges LIMIT ?");
319838
+ const edgesStmt = db.prepare(`
319839
+ SELECT ge.* FROM graph_edges ge
319840
+ WHERE ge.from_node_id IN (
319841
+ SELECT id FROM graph_nodes
319842
+ ORDER BY
319843
+ CASE type ${typeOrderCase} ELSE 999 END,
319844
+ created_at DESC
319845
+ LIMIT ?
319846
+ )
319847
+ AND ge.to_node_id IN (
319848
+ SELECT id FROM graph_nodes
319849
+ ORDER BY
319850
+ CASE type ${typeOrderCase} ELSE 999 END,
319851
+ created_at DESC
319852
+ LIMIT ?
319853
+ )
319854
+ LIMIT ?
319855
+ `);
319504
319856
  const countResult = countStmt.get();
319857
+ const edgeCountResult = edgeCountStmt.get();
319505
319858
  const nodeRows = nodesStmt.all(maxNodes);
319506
- const edgeRows = edgesStmt.all(maxEdges);
319859
+ const edgeRows = edgesStmt.all(maxNodes, maxNodes, maxEdges);
319507
319860
  return {
319508
319861
  totalNodeCount: countResult.count,
319862
+ totalEdgeCount: edgeCountResult.count,
319509
319863
  nodes: nodeRows.map((row) => ({
319510
319864
  id: String(row.id),
319511
319865
  label: String(row.label),
@@ -319883,10 +320237,12 @@ var init_server3 = __esm({
319883
320237
  const id = c.req.param("id");
319884
320238
  const workspace = getRegistryWorkspace(id);
319885
320239
  if (!workspace) return c.json(null);
319886
- const { nodes: nodeRows, edges: edgeRows } = getWorkspaceGraphOptimized(
320240
+ const maxNodes = Math.min(parseInt(c.req.query("limit") ?? "25000", 10) || 25e3, 25e3);
320241
+ const maxEdges = Math.min(parseInt(c.req.query("edgeLimit") ?? "75000", 10) || 75e3, 75e3);
320242
+ const { nodes: nodeRows, edges: edgeRows, totalNodeCount, totalEdgeCount } = getWorkspaceGraphOptimized(
319887
320243
  workspace.rootPath,
319888
- 300,
319889
- 1e3
320244
+ maxNodes,
320245
+ maxEdges
319890
320246
  );
319891
320247
  const degreeMap = /* @__PURE__ */ new Map();
319892
320248
  for (const edge of edgeRows) {
@@ -319921,8 +320277,10 @@ var init_server3 = __esm({
319921
320277
  edges,
319922
320278
  nodeTypes,
319923
320279
  edgeTypes,
319924
- totalNodes: nodes.length,
319925
- totalEdges: edges.length
320280
+ totalNodes: totalNodeCount,
320281
+ totalEdges: totalEdgeCount,
320282
+ displayedNodes: nodes.length,
320283
+ displayedEdges: edges.length
319926
320284
  });
319927
320285
  });
319928
320286
  app.post("/api/query", async (c) => {
@@ -320128,7 +320486,7 @@ ${codeblock}`, options);
320128
320486
  });
320129
320487
 
320130
320488
  // ../../node_modules/.pnpm/smol-toml@1.6.1/node_modules/smol-toml/dist/util.js
320131
- function isEscaped(str2, ptr) {
320489
+ function isEscaped2(str2, ptr) {
320132
320490
  let i = 0;
320133
320491
  while (str2[ptr - ++i] === "\\")
320134
320492
  ;
@@ -320193,7 +320551,7 @@ function getStringEnd(str2, seek) {
320193
320551
  seek += target.length - 1;
320194
320552
  do
320195
320553
  seek = str2.indexOf(target, ++seek);
320196
- while (seek > -1 && first !== "'" && isEscaped(str2, seek));
320554
+ while (seek > -1 && first !== "'" && isEscaped2(str2, seek));
320197
320555
  if (seek > -1) {
320198
320556
  seek += target.length;
320199
320557
  if (target.length > 1) {
@@ -321325,8 +321683,9 @@ var {
321325
321683
  // src/cli.ts
321326
321684
  init_src();
321327
321685
  init_src4();
321686
+ var pkg = JSON.parse(import_node_fs17.default.readFileSync(import_node_path21.default.resolve(__dirname, "../package.json"), "utf-8"));
321328
321687
  var program2 = new Command();
321329
- program2.name("openez").description("OpenEZ Graph - Local-first knowledge retrieval system").version("0.4.0");
321688
+ program2.name("openez").description("OpenEZ Graph - Local-first knowledge retrieval system").version(pkg.version);
321330
321689
  program2.command("init").description("Initialize a workspace at the given path and run initial index").argument("[path]", "path to the project directory", process.cwd()).option("--no-index", "skip initial indexing").action(async (targetPath, options) => {
321331
321690
  const resolvedPath = import_node_path21.default.resolve(targetPath);
321332
321691
  if (!import_node_fs17.default.existsSync(resolvedPath)) {