grepmax 0.7.14 → 0.7.16

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.
@@ -891,6 +891,21 @@ exports.mcp = new commander_1.Command("mcp")
891
891
  const lines = [];
892
892
  // Center
893
893
  lines.push(`${graph.center.symbol} [${graph.center.role}] ${graph.center.file}:${graph.center.line + 1}`);
894
+ // Importers
895
+ if (graph.importers.length > 0) {
896
+ // Filter out the file where the symbol is defined
897
+ const centerFile = graph.center.file;
898
+ const filteredImporters = graph.importers.filter((p) => p !== centerFile);
899
+ if (filteredImporters.length > 0) {
900
+ lines.push("Imported by:");
901
+ for (const imp of filteredImporters.slice(0, 10)) {
902
+ const rel = imp.startsWith(projectRoot)
903
+ ? imp.slice(projectRoot.length + 1)
904
+ : imp;
905
+ lines.push(` ${rel}`);
906
+ }
907
+ }
908
+ }
894
909
  // Callers (recursive tree)
895
910
  function formatCallerTree(trees, indent) {
896
911
  for (const t of trees) {
@@ -105,19 +105,40 @@ class GraphBuilder {
105
105
  return { center, callers, callees: calleeNodes };
106
106
  });
107
107
  }
108
+ getImporters(symbol) {
109
+ return __awaiter(this, void 0, void 0, function* () {
110
+ const table = yield this.db.ensureTable();
111
+ const escaped = (0, filter_builder_1.escapeSqlString)(symbol);
112
+ const rows = yield table
113
+ .query()
114
+ .select(["path"])
115
+ .where(`content LIKE '%import%${escaped}%'`)
116
+ .limit(100)
117
+ .toArray();
118
+ const files = new Set();
119
+ for (const row of rows) {
120
+ files.add(String(row.path || ""));
121
+ }
122
+ return Array.from(files);
123
+ });
124
+ }
108
125
  buildGraphMultiHop(symbol, depth) {
109
126
  return __awaiter(this, void 0, void 0, function* () {
110
- const graph = yield this.buildGraph(symbol);
127
+ const [graph, importers] = yield Promise.all([
128
+ this.buildGraph(symbol),
129
+ this.getImporters(symbol),
130
+ ]);
111
131
  if (depth <= 1 || !graph.center) {
112
132
  return {
113
133
  center: graph.center,
114
134
  callerTree: graph.callers.map((c) => ({ node: c, callers: [] })),
115
135
  callees: graph.callees,
136
+ importers,
116
137
  };
117
138
  }
118
139
  const visited = new Set([symbol]);
119
140
  const callerTree = yield this.expandCallers(graph.callers, depth - 1, visited);
120
- return { center: graph.center, callerTree, callees: graph.callees };
141
+ return { center: graph.center, callerTree, callees: graph.callees, importers };
121
142
  });
122
143
  }
123
144
  expandCallers(callers, remainingDepth, visited) {
@@ -58,7 +58,7 @@ const logger_1 = require("../utils/logger");
58
58
  const meta_cache_1 = require("../store/meta-cache");
59
59
  const vector_db_1 = require("../store/vector-db");
60
60
  const filter_builder_1 = require("../utils/filter-builder");
61
- const file_utils_1 = require("../utils/file-utils");
61
+ // isIndexableFile no longer used — extension check inlined for performance
62
62
  const lock_1 = require("../utils/lock");
63
63
  const project_registry_1 = require("../utils/project-registry");
64
64
  const project_root_1 = require("../utils/project-root");
@@ -329,19 +329,13 @@ function initialSync(options) {
329
329
  break;
330
330
  }
331
331
  const absPath = path.join(paths.root, relPath);
332
- // Check real path to avoid duplicates and loops
333
- try {
334
- const realPath = fs.realpathSync(absPath);
335
- if (visitedRealPaths.has(realPath))
336
- continue;
337
- visitedRealPaths.add(realPath);
338
- }
339
- catch (_g) {
340
- // Skip broken symlinks or inaccessible files
332
+ // Extension check only no stat syscall
333
+ const ext = path.extname(absPath).toLowerCase();
334
+ const basename = path.basename(absPath).toLowerCase();
335
+ if (!config_1.INDEXABLE_EXTENSIONS.has(ext) &&
336
+ !config_1.INDEXABLE_EXTENSIONS.has(basename)) {
341
337
  continue;
342
338
  }
343
- if (!(0, file_utils_1.isIndexableFile)(absPath))
344
- continue;
345
339
  walkedFiles++;
346
340
  yield schedule(() => __awaiter(this, void 0, void 0, function* () {
347
341
  if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
@@ -349,8 +343,20 @@ function initialSync(options) {
349
343
  return;
350
344
  }
351
345
  try {
352
- const stats = yield fs.promises.stat(absPath);
353
- if (!(0, file_utils_1.isIndexableFile)(absPath, stats.size)) {
346
+ // Stat + symlink dedup (lstat to detect symlinks without resolving)
347
+ const stats = yield fs.promises.lstat(absPath);
348
+ if (stats.isSymbolicLink()) {
349
+ try {
350
+ const realPath = yield fs.promises.realpath(absPath);
351
+ if (visitedRealPaths.has(realPath))
352
+ return;
353
+ visitedRealPaths.add(realPath);
354
+ }
355
+ catch (_a) {
356
+ return; // Broken symlink
357
+ }
358
+ }
359
+ if (!stats.isFile() || stats.size === 0 || stats.size > config_1.MAX_FILE_SIZE_BYTES) {
354
360
  return;
355
361
  }
356
362
  // Use absolute path as the key for MetaCache
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "grepmax",
3
- "version": "0.7.14",
3
+ "version": "0.7.16",
4
4
  "author": "Robert Owens <robowens@me.com>",
5
5
  "homepage": "https://github.com/reowens/grepmax",
6
6
  "bugs": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "grepmax",
3
- "version": "0.7.14",
3
+ "version": "0.7.16",
4
4
  "description": "Semantic code search for Claude Code. Automatically indexes your project and provides intelligent search capabilities.",
5
5
  "author": {
6
6
  "name": "Robert Owens",
@@ -69,10 +69,12 @@ File or directory structure — signatures with bodies collapsed (~4x fewer toke
69
69
  - `limit` (optional): Max files for directory mode (default 10, max 20)
70
70
 
71
71
  ### trace_calls
72
- Call graph — who calls a symbol and what it calls. Callers and callees include file:line locations. Unscoped — follows calls across all indexed directories.
72
+ Call graph — who imports a symbol, who calls it, and what it calls. Includes file:line locations. Unscoped — follows calls across all indexed directories.
73
73
  - `symbol` (required): Function/method/class name
74
74
  - `depth` (optional): Traversal depth for callers (default 1, max 3). depth: 2 shows callers-of-callers with indentation.
75
75
 
76
+ Output: definition, "Imported by" (files with import statements), "Callers" (functions that call it), "Calls" (what it calls).
77
+
76
78
  ### list_symbols
77
79
  List indexed symbols with definition locations, role, and export status.
78
80
  - `pattern` (optional): Filter by name (case-insensitive substring match)