grepmax 0.21.2 → 0.21.3

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.
@@ -10,7 +10,7 @@
10
10
  "name": "grepmax",
11
11
  "source": "./plugins/grepmax",
12
12
  "description": "Semantic code search for Claude Code. Automatically indexes your project and provides intelligent search capabilities.",
13
- "version": "0.21.2",
13
+ "version": "0.21.3",
14
14
  "author": {
15
15
  "name": "Robert Owens",
16
16
  "email": "robowens@me.com"
package/dist/config.js CHANGED
@@ -84,7 +84,7 @@ exports.CONFIG = {
84
84
  // reindex to take effect. Must equal the latest entry's `v` in
85
85
  // CHUNKER_VERSION_HISTORY below — see that list for per-version severity and
86
86
  // the user-facing note rendered by `gmax doctor` and the staleness hint.
87
- CHUNKER_VERSION: 3,
87
+ CHUNKER_VERSION: 4,
88
88
  };
89
89
  /**
90
90
  * Per-version record of what changed in the chunker and how much it matters to
@@ -105,6 +105,11 @@ exports.CHUNKER_VERSION_HISTORY = [
105
105
  severity: "additive",
106
106
  note: "type-position edges; dead/trace miss type-only callers until reindex.",
107
107
  },
108
+ {
109
+ v: 4,
110
+ severity: "additive",
111
+ note: "member-call edges recorded separately (obj.method()); substrate for receiver-aware call resolution, no query effect until reindex.",
112
+ },
108
113
  ];
109
114
  /**
110
115
  * Describe the gap between an index's stamped chunker version and the current
@@ -13,6 +13,7 @@ exports.GraphBuilder = void 0;
13
13
  const languages_1 = require("../core/languages");
14
14
  const filter_builder_1 = require("../utils/filter-builder");
15
15
  const query_timeout_1 = require("../utils/query-timeout");
16
+ const callsites_1 = require("./callsites");
16
17
  const graph_traversal_1 = require("./graph-traversal");
17
18
  class GraphBuilder {
18
19
  constructor(db, pathPrefix, excludePrefixes) {
@@ -130,9 +131,14 @@ class GraphBuilder {
130
131
  const callers = yield this.getCallers(symbol, anchorFamily);
131
132
  // 3. Get Callees — resolve each to a GraphNode with file:line
132
133
  const calleeNames = center ? center.calls.slice(0, 15) : [];
134
+ const centerFile = center ? center.file : "";
133
135
  const calleeNodes = [];
134
136
  for (const name of calleeNames) {
135
137
  const esc = (0, filter_builder_1.escapeSqlString)(name);
138
+ // Pull a few candidates (was .limit(1)) so a callee name defined in more
139
+ // than one file can prefer the center's OWN file instead of an arbitrary
140
+ // same-named definition in an unrelated module — the cheapest correct
141
+ // disambiguation, and strictly better than the old first-row guess.
136
142
  const rows = yield table
137
143
  .query()
138
144
  .where(this.scopeWhere(`array_contains(defined_symbols, '${esc}')`))
@@ -145,12 +151,21 @@ class GraphBuilder {
145
151
  "parent_symbol",
146
152
  "complexity",
147
153
  ])
148
- .limit(1)
154
+ .limit(25)
149
155
  .toArray();
150
156
  if (rows.length > 0) {
151
- calleeNodes.push(this.mapRowToNode(rows[0], name, "center"));
157
+ // Prefer-self-file: a callee the center also defines locally is almost
158
+ // certainly that local definition. Falls back to the first row when no
159
+ // candidate is in the center's file (unchanged behaviour then).
160
+ const selfRow = rows.find((r) => { var _a; return String((_a = r.path) !== null && _a !== void 0 ? _a : "") === centerFile; });
161
+ calleeNodes.push(this.mapRowToNode((selfRow !== null && selfRow !== void 0 ? selfRow : rows[0]), name, "center"));
152
162
  }
153
- else {
163
+ else if (!(0, callsites_1.isBuiltinCallee)(name)) {
164
+ // Unresolved + a known builtin (.map/.get/forEach) → suppress the
165
+ // phantom callee edge instead of emitting a "(not indexed)" stub.
166
+ // Mirrors the display-layer guard (peek.ts: `c.file || !isBuiltinCallee`):
167
+ // a project symbol that shadows a builtin name still resolves above and
168
+ // is kept, so only genuine builtins are dropped.
154
169
  calleeNodes.push({
155
170
  symbol: name,
156
171
  file: "",
@@ -274,6 +289,11 @@ class GraphBuilder {
274
289
  const out = [];
275
290
  for (const h of hits) {
276
291
  const loc = yield this.resolveLocation(h.symbol, anchorFamily);
292
+ // Drop unresolved builtins (.map/.get/forEach) reached via callee edges —
293
+ // same resolution-aware rule as buildGraph/peek. A neighbor that resolves
294
+ // to an indexed definition is kept even if it shadows a builtin name.
295
+ if (!loc && (0, callsites_1.isBuiltinCallee)(h.symbol))
296
+ continue;
277
297
  out.push(Object.assign(Object.assign({}, h), { file: (_a = loc === null || loc === void 0 ? void 0 : loc.file) !== null && _a !== void 0 ? _a : "", line: (_b = loc === null || loc === void 0 ? void 0 : loc.line) !== null && _b !== void 0 ? _b : 0 }));
278
298
  }
279
299
  return out;
@@ -571,6 +571,15 @@ class TreeSitterChunker {
571
571
  typeReferencedSymbols.push(n);
572
572
  }
573
573
  };
574
+ // Member-call names (`obj.method()`), recorded additively alongside
575
+ // referencedSymbols (see Chunk.memberReferencedSymbols). Deduped, like
576
+ // type refs — a future resolver queries membership, not occurrence count.
577
+ const memberReferencedSymbols = [];
578
+ const addMemberRef = (n) => {
579
+ if (n && !memberReferencedSymbols.includes(n)) {
580
+ memberReferencedSymbols.push(n);
581
+ }
582
+ };
574
583
  // Leaf identifier node types across grammars (a bare name with no
575
584
  // named children — `ErrorCodes`, not `a.ErrorCodes`).
576
585
  const LEAF_ID_TYPES = new Set([
@@ -677,24 +686,33 @@ class TreeSitterChunker {
677
686
  : null;
678
687
  if (func) {
679
688
  let funcName = func.text;
689
+ let isMemberCall = false;
680
690
  // Handle member access (obj.method) to extract just 'method'
681
691
  if (func.type === "member_expression") {
682
692
  // JS/TS: object.property
683
693
  const prop = func.childForFieldName
684
694
  ? func.childForFieldName("property")
685
695
  : null;
686
- if (prop)
696
+ if (prop) {
687
697
  funcName = prop.text;
698
+ isMemberCall = true;
699
+ }
688
700
  }
689
701
  else if (func.type === "attribute") {
690
702
  // Python: object.attribute
691
703
  const attr = func.childForFieldName
692
704
  ? func.childForFieldName("attribute")
693
705
  : null;
694
- if (attr)
706
+ if (attr) {
695
707
  funcName = attr.text;
708
+ isMemberCall = true;
709
+ }
696
710
  }
697
711
  referencedSymbols.push(funcName);
712
+ // Additive: member calls ALSO go here. referencedSymbols is left
713
+ // untouched (no recall loss); this just tags which were members.
714
+ if (isMemberCall)
715
+ addMemberRef(funcName);
698
716
  }
699
717
  else {
700
718
  // Swift/Kotlin: call_expression has no "function" field;
@@ -702,15 +720,20 @@ class TreeSitterChunker {
702
720
  const firstChild = ((_a = n.namedChildren) !== null && _a !== void 0 ? _a : [])[0];
703
721
  if (firstChild) {
704
722
  let funcName = firstChild.text;
723
+ let isMemberCall = false;
705
724
  if (firstChild.type === "navigation_expression") {
706
725
  const suffix = ((_b = firstChild.namedChildren) !== null && _b !== void 0 ? _b : []).find((c) => c.type === "navigation_suffix");
707
726
  const methodId = suffix
708
727
  ? ((_c = suffix.namedChildren) !== null && _c !== void 0 ? _c : []).find((c) => c.type === "simple_identifier")
709
728
  : null;
710
- if (methodId)
729
+ if (methodId) {
711
730
  funcName = methodId.text;
731
+ isMemberCall = true;
732
+ }
712
733
  }
713
734
  referencedSymbols.push(funcName);
735
+ if (isMemberCall)
736
+ addMemberRef(funcName);
714
737
  }
715
738
  }
716
739
  }
@@ -905,6 +928,7 @@ class TreeSitterChunker {
905
928
  definedSymbols,
906
929
  referencedSymbols,
907
930
  typeReferencedSymbols,
931
+ memberReferencedSymbols,
908
932
  role,
909
933
  parentSymbol: stack.length > 1
910
934
  ? stack[stack.length - 1].replace(/^(Class|Method|Function|Interface|Type): /, "")
@@ -990,6 +1014,9 @@ class TreeSitterChunker {
990
1014
  if (sub.typeReferencedSymbols) {
991
1015
  sub.typeReferencedSymbols = sub.typeReferencedSymbols.filter(occurs);
992
1016
  }
1017
+ if (sub.memberReferencedSymbols) {
1018
+ sub.memberReferencedSymbols = sub.memberReferencedSymbols.filter(occurs);
1019
+ }
993
1020
  return sub;
994
1021
  }
995
1022
  splitIfTooBig(chunk) {
@@ -271,6 +271,7 @@ class VectorDB {
271
271
  defined_symbols: [],
272
272
  referenced_symbols: [],
273
273
  type_referenced_symbols: [],
274
+ member_referenced_symbols: [],
274
275
  imports: [],
275
276
  exports: [],
276
277
  role: "",
@@ -340,6 +341,7 @@ class VectorDB {
340
341
  new apache_arrow_1.Field("defined_symbols", new apache_arrow_1.List(new apache_arrow_1.Field("item", new apache_arrow_1.Utf8(), true)), true),
341
342
  new apache_arrow_1.Field("referenced_symbols", new apache_arrow_1.List(new apache_arrow_1.Field("item", new apache_arrow_1.Utf8(), true)), true),
342
343
  new apache_arrow_1.Field("type_referenced_symbols", new apache_arrow_1.List(new apache_arrow_1.Field("item", new apache_arrow_1.Utf8(), true)), true),
344
+ new apache_arrow_1.Field("member_referenced_symbols", new apache_arrow_1.List(new apache_arrow_1.Field("item", new apache_arrow_1.Utf8(), true)), true),
343
345
  new apache_arrow_1.Field("imports", new apache_arrow_1.List(new apache_arrow_1.Field("item", new apache_arrow_1.Utf8(), true)), true),
344
346
  new apache_arrow_1.Field("exports", new apache_arrow_1.List(new apache_arrow_1.Field("item", new apache_arrow_1.Utf8(), true)), true),
345
347
  new apache_arrow_1.Field("role", new apache_arrow_1.Utf8(), true),
@@ -360,16 +362,27 @@ class VectorDB {
360
362
  return __awaiter(this, void 0, void 0, function* () {
361
363
  const schema = yield table.schema();
362
364
  const fields = new Set(schema.fields.map((f) => f.name));
363
- if (fields.has("type_referenced_symbols"))
364
- return;
365
- try {
366
- yield table.addColumns(new apache_arrow_1.Field("type_referenced_symbols", new apache_arrow_1.List(new apache_arrow_1.Field("item", new apache_arrow_1.Utf8(), true)), true));
367
- (0, logger_1.log)("db", "Added type_referenced_symbols column to existing table");
368
- }
369
- catch (err) {
370
- // Lost a race with another writer that already added it, or a transient
371
- // commit conflict — the next ensureTable() re-checks and no-ops.
372
- (0, logger_1.debug)("vectordb", `evolveSchema skipped: ${err.message}`);
365
+ // Additive list columns that older tables may predate. Each is checked and
366
+ // added INDEPENDENTLY: a single early-return on the first existing column
367
+ // (as this used to do) would permanently strand every table that already has
368
+ // `type_referenced_symbols` i.e. all of them — without ever gaining a newer
369
+ // column like `member_referenced_symbols`.
370
+ const additiveListColumns = [
371
+ "type_referenced_symbols",
372
+ "member_referenced_symbols",
373
+ ];
374
+ for (const col of additiveListColumns) {
375
+ if (fields.has(col))
376
+ continue;
377
+ try {
378
+ yield table.addColumns(new apache_arrow_1.Field(col, new apache_arrow_1.List(new apache_arrow_1.Field("item", new apache_arrow_1.Utf8(), true)), true));
379
+ (0, logger_1.log)("db", `Added ${col} column to existing table`);
380
+ }
381
+ catch (err) {
382
+ // Lost a race with another writer that already added it, or a transient
383
+ // commit conflict — the next ensureTable() re-checks and no-ops.
384
+ (0, logger_1.debug)("vectordb", `evolveSchema ${col} skipped: ${err.message}`);
385
+ }
373
386
  }
374
387
  });
375
388
  }
@@ -395,7 +408,7 @@ class VectorDB {
395
408
  }
396
409
  insertBatch(records) {
397
410
  return __awaiter(this, void 0, void 0, function* () {
398
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
411
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r;
399
412
  if (!records.length)
400
413
  return;
401
414
  this.ensureDiskOk();
@@ -473,12 +486,14 @@ class VectorDB {
473
486
  rec.defined_symbols = (_g = rec.defined_symbols) !== null && _g !== void 0 ? _g : [];
474
487
  rec.referenced_symbols = (_h = rec.referenced_symbols) !== null && _h !== void 0 ? _h : [];
475
488
  rec.type_referenced_symbols = (_j = rec.type_referenced_symbols) !== null && _j !== void 0 ? _j : [];
476
- rec.imports = (_k = rec.imports) !== null && _k !== void 0 ? _k : [];
477
- rec.exports = (_l = rec.exports) !== null && _l !== void 0 ? _l : [];
478
- rec.role = (_m = rec.role) !== null && _m !== void 0 ? _m : "";
479
- rec.parent_symbol = (_o = rec.parent_symbol) !== null && _o !== void 0 ? _o : "";
480
- rec.file_skeleton = (_p = rec.file_skeleton) !== null && _p !== void 0 ? _p : "";
481
- rec.summary = (_q = rec.summary) !== null && _q !== void 0 ? _q : null;
489
+ rec.member_referenced_symbols =
490
+ (_k = rec.member_referenced_symbols) !== null && _k !== void 0 ? _k : [];
491
+ rec.imports = (_l = rec.imports) !== null && _l !== void 0 ? _l : [];
492
+ rec.exports = (_m = rec.exports) !== null && _m !== void 0 ? _m : [];
493
+ rec.role = (_o = rec.role) !== null && _o !== void 0 ? _o : "";
494
+ rec.parent_symbol = (_p = rec.parent_symbol) !== null && _p !== void 0 ? _p : "";
495
+ rec.file_skeleton = (_q = rec.file_skeleton) !== null && _q !== void 0 ? _q : "";
496
+ rec.summary = (_r = rec.summary) !== null && _r !== void 0 ? _r : null;
482
497
  }
483
498
  try {
484
499
  yield this.withWriteGate(() => table.add(records));
@@ -227,6 +227,7 @@ class WorkerOrchestrator {
227
227
  defined_symbols: chunk.definedSymbols,
228
228
  referenced_symbols: chunk.referencedSymbols,
229
229
  type_referenced_symbols: chunk.typeReferencedSymbols,
230
+ member_referenced_symbols: chunk.memberReferencedSymbols,
230
231
  role: chunk.role,
231
232
  parent_symbol: chunk.parentSymbol,
232
233
  file_skeleton: chunk.isAnchor ? skeleton : undefined,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "grepmax",
3
- "version": "0.21.2",
3
+ "version": "0.21.3",
4
4
  "author": "Robert Owens <78518764+reowens@users.noreply.github.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.21.2",
3
+ "version": "0.21.3",
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",