grepmax 0.17.23 → 0.18.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.
@@ -46,11 +46,11 @@ exports.audit = void 0;
46
46
  exports.computeAudit = computeAudit;
47
47
  const commander_1 = require("commander");
48
48
  const callsites_1 = require("../lib/graph/callsites");
49
+ const vector_db_1 = require("../lib/store/vector-db");
49
50
  const arrow_1 = require("../lib/utils/arrow");
50
51
  const exit_1 = require("../lib/utils/exit");
51
52
  const project_registry_1 = require("../lib/utils/project-registry");
52
53
  const project_root_1 = require("../lib/utils/project-root");
53
- const vector_db_1 = require("../lib/store/vector-db");
54
54
  const useColors = process.stdout.isTTY && !process.env.NO_COLOR;
55
55
  const style = {
56
56
  bold: (s) => (useColors ? `\x1b[1m${s}\x1b[22m` : s),
@@ -291,6 +291,7 @@ exports.audit = new commander_1.Command("audit")
291
291
  "start_line",
292
292
  "defined_symbols",
293
293
  "referenced_symbols",
294
+ "type_referenced_symbols",
294
295
  "is_exported",
295
296
  ])
296
297
  .where(buildScopeWhere(scope))
@@ -308,7 +309,14 @@ exports.audit = new commander_1.Command("audit")
308
309
  start_line: Number(r.start_line || 0),
309
310
  is_exported: Boolean(r.is_exported),
310
311
  defined_symbols: (0, arrow_1.toArr)(r.defined_symbols),
311
- referenced_symbols: (0, arrow_1.toArr)(r.referenced_symbols),
312
+ // Union call-position + type-position edges so god-node ranking and
313
+ // dead-candidate detection see references made purely in type position.
314
+ referenced_symbols: [
315
+ ...new Set([
316
+ ...(0, arrow_1.toArr)(r.referenced_symbols),
317
+ ...(0, arrow_1.toArr)(r.type_referenced_symbols),
318
+ ]),
319
+ ],
312
320
  })), prefix, top);
313
321
  console.log(opts.agent ? formatAgent(result) : formatHuman(result));
314
322
  }
@@ -50,6 +50,7 @@ const agent_errors_1 = require("../lib/utils/agent-errors");
50
50
  const exit_1 = require("../lib/utils/exit");
51
51
  const filter_builder_1 = require("../lib/utils/filter-builder");
52
52
  const project_registry_1 = require("../lib/utils/project-registry");
53
+ const stale_hint_1 = require("../lib/utils/stale-hint");
53
54
  const project_root_1 = require("../lib/utils/project-root");
54
55
  const useColors = process.stdout.isTTY && !process.env.NO_COLOR;
55
56
  const style = {
@@ -118,6 +119,8 @@ exports.dead = new commander_1.Command("dead")
118
119
  let vectorDb = null;
119
120
  try {
120
121
  const projectRoot = (_a = (0, project_root_1.findProjectRoot)(root)) !== null && _a !== void 0 ? _a : root;
122
+ (0, stale_hint_1.maybeWarnStaleChunker)(projectRoot, { agent: opts.agent });
123
+ (0, stale_hint_1.maybeWarnStaleEmbedding)(projectRoot, { agent: opts.agent });
121
124
  const paths = (0, project_root_1.ensureProjectPaths)(projectRoot);
122
125
  vectorDb = new vector_db_1.VectorDB(paths.lancedbDir);
123
126
  const { resolveScope, buildScopeWhere } = yield Promise.resolve().then(() => __importStar(require("../lib/utils/scope-filter")));
@@ -85,7 +85,7 @@ exports.doctor = new commander_1.Command("doctor")
85
85
  .option("--fix", "Auto-fix detected issues (compact, prune, remove stale locks)", false)
86
86
  .option("--agent", "Compact output for AI agents", false)
87
87
  .action((opts) => __awaiter(void 0, void 0, void 0, function* () {
88
- var _a, _b, _c, _d;
88
+ var _a, _b, _c, _d, _e, _f;
89
89
  if (!opts.agent)
90
90
  console.log("gmax Doctor\n");
91
91
  const root = config_1.PATHS.globalRoot;
@@ -236,12 +236,18 @@ exports.doctor = new commander_1.Command("doctor")
236
236
  availBytes = diskStats.bavail * diskStats.bsize;
237
237
  diskLevel = availBytes < config_1.DISK_CRITICAL_BYTES ? "CRITICAL" : availBytes < config_1.DISK_LOW_BYTES ? "LOW" : "ok";
238
238
  }
239
- catch (_e) { }
239
+ catch (_g) { }
240
240
  const staleChunkerProjects = projects.filter((p) => {
241
241
  var _a;
242
242
  return p.status === "indexed" &&
243
243
  ((_a = p.chunkerVersion) !== null && _a !== void 0 ? _a : 1) < config_1.CONFIG.CHUNKER_VERSION;
244
244
  });
245
+ // Projects whose stored embedding model/dim no longer matches the global
246
+ // config. Visibility only — recovery is a manual `gmax index --reset`
247
+ // (a dim change can't be auto-fixed in the shared fixed-dim table; that's
248
+ // the deferred Phase 1B re-embed work).
249
+ const staleEmbeddingProjects = projects.filter((p) => p.status === "indexed" &&
250
+ (0, config_1.describeEmbeddingGap)({ modelTier: p.modelTier, vectorDim: p.vectorDim }, { modelTier: globalConfig.modelTier, vectorDim: globalConfig.vectorDim }) !== null);
245
251
  if (opts.agent) {
246
252
  const fields = [
247
253
  "index_health",
@@ -257,12 +263,38 @@ exports.doctor = new commander_1.Command("doctor")
257
263
  `daemon=${daemonUp ? "running" : "stopped"}`,
258
264
  `orphaned=${orphanedProjects.length}`,
259
265
  `stale_chunker=${staleChunkerProjects.length}`,
266
+ `stale_embedding=${staleEmbeddingProjects.length}`,
260
267
  ];
261
268
  console.log(fields.join("\t"));
262
- if (staleChunkerProjects.length > 0) {
263
- console.log(`stale_chunker_fix: run 'gmax index --reset' in: ${staleChunkerProjects
264
- .map((p) => p.name || path.basename(p.root))
265
- .join(", ")}`);
269
+ for (const p of staleChunkerProjects) {
270
+ const gap = (0, config_1.describeChunkerGap)(p.chunkerVersion);
271
+ if (!gap)
272
+ continue;
273
+ console.log([
274
+ "stale_chunker_project",
275
+ `name=${p.name || path.basename(p.root)}`,
276
+ `indexed_v=${gap.fromVersion}`,
277
+ `current_v=${gap.toVersion}`,
278
+ `severity=${gap.severity}`,
279
+ `note=${gap.notes.join("; ")}`,
280
+ `fix=gmax index --reset (in ${p.root})`,
281
+ ].join("\t"));
282
+ }
283
+ for (const p of staleEmbeddingProjects) {
284
+ const gap = (0, config_1.describeEmbeddingGap)({ modelTier: p.modelTier, vectorDim: p.vectorDim }, { modelTier: globalConfig.modelTier, vectorDim: globalConfig.vectorDim });
285
+ if (!gap)
286
+ continue;
287
+ console.log([
288
+ "stale_embedding_project",
289
+ `name=${p.name || path.basename(p.root)}`,
290
+ `indexed_model=${gap.fromModel}`,
291
+ `current_model=${gap.toModel}`,
292
+ `indexed_dim=${gap.fromDim}`,
293
+ `current_dim=${gap.toDim}`,
294
+ `dim_changed=${gap.dimChanged}`,
295
+ `severity=${gap.severity}`,
296
+ `fix=gmax index --reset (in ${p.root})`,
297
+ ].join("\t"));
266
298
  }
267
299
  }
268
300
  else {
@@ -307,13 +339,42 @@ exports.doctor = new commander_1.Command("doctor")
307
339
  }
308
340
  // Daemon
309
341
  console.log(`${daemonUp ? "ok" : "INFO"} Daemon: ${daemonUp ? "running" : "not running"}`);
310
- // Index built by an older chunker — graph metadata fixes need a reindex
342
+ // Index built by an older chunker — graph metadata fixes need a reindex.
343
+ // Severity (and the WARN/INFO label) is driven by CHUNKER_VERSION_HISTORY:
344
+ // a breaking gap means stale metadata is wrong, an additive gap only
345
+ // means newer edges are missing.
311
346
  if (staleChunkerProjects.length > 0) {
312
- console.log(`WARN Stale chunker: ${staleChunkerProjects.length} project(s) indexed before chunker v${config_1.CONFIG.CHUNKER_VERSION} graph commands (peek/impact/trace) may overcount callers`);
347
+ const anyBreaking = staleChunkerProjects.some((p) => { var _a; return ((_a = (0, config_1.describeChunkerGap)(p.chunkerVersion)) === null || _a === void 0 ? void 0 : _a.severity) === "breaking"; });
348
+ console.log(`${anyBreaking ? "WARN" : "INFO"} Stale chunker: ${staleChunkerProjects.length} project(s) indexed before chunker v${config_1.CONFIG.CHUNKER_VERSION} — run 'gmax doctor --fix' to reindex`);
313
349
  for (const p of staleChunkerProjects) {
314
- console.log(` - ${p.name || path.basename(p.root)}: run 'gmax index --reset' in ${p.root}`);
350
+ const gap = (0, config_1.describeChunkerGap)(p.chunkerVersion);
351
+ if (!gap)
352
+ continue;
353
+ console.log(` - ${p.name || path.basename(p.root)} (v${gap.fromVersion}→v${gap.toVersion}, ${gap.severity}): ${gap.notes.join(" ")}`);
354
+ console.log(` run 'gmax index --reset' in ${p.root}`);
315
355
  }
316
356
  }
357
+ // Index built with a different embedding model/dim than the current
358
+ // config. A dim change is breaking (search scores are invalid until a
359
+ // re-embed); a same-dim model swap is additive. Unlike the stale-chunker
360
+ // case this is NOT auto-fixed by `--fix`: a dim change can't coexist in
361
+ // the shared fixed-dim table (deferred Phase 1B), so we point at the
362
+ // manual reset instead.
363
+ if (staleEmbeddingProjects.length > 0) {
364
+ const gaps = staleEmbeddingProjects.map((p) => (0, config_1.describeEmbeddingGap)({ modelTier: p.modelTier, vectorDim: p.vectorDim }, { modelTier: globalConfig.modelTier, vectorDim: globalConfig.vectorDim }));
365
+ const anyBreaking = gaps.some((g) => (g === null || g === void 0 ? void 0 : g.severity) === "breaking");
366
+ console.log(`${anyBreaking ? "WARN" : "INFO"} Stale embedding: ${staleEmbeddingProjects.length} project(s) indexed with a different embedding model/dim — run 'gmax index --reset' per project`);
367
+ staleEmbeddingProjects.forEach((p, i) => {
368
+ const gap = gaps[i];
369
+ if (!gap)
370
+ return;
371
+ const change = gap.dimChanged
372
+ ? `${gap.fromDim}d→${gap.toDim}d`
373
+ : `model ${gap.fromModel}→${gap.toModel}`;
374
+ console.log(` - ${p.name || path.basename(p.root)} (${change}, ${gap.severity})`);
375
+ console.log(` run 'gmax index --reset' in ${p.root}`);
376
+ });
377
+ }
317
378
  // Projects
318
379
  if (orphanedProjects.length > 0) {
319
380
  console.log(`WARN Orphaned projects: ${orphanedProjects.length} (directories no longer exist)`);
@@ -342,7 +403,7 @@ exports.doctor = new commander_1.Command("doctor")
342
403
  }
343
404
  yield mc.close();
344
405
  }
345
- catch (_f) { }
406
+ catch (_h) { }
346
407
  }
347
408
  }
348
409
  // --fix auto-remediation
@@ -372,6 +433,38 @@ exports.doctor = new commander_1.Command("doctor")
372
433
  console.log(`ok Removed ${orphanedProjects.length} orphaned project(s) from registry`);
373
434
  fixed++;
374
435
  }
436
+ // Reindex projects whose index predates the current chunker. This is a
437
+ // full `--reset` reindex per project (routed through the daemon), so it
438
+ // can be slow on large repos — unlike the cheap fixes above.
439
+ if (staleChunkerProjects.length > 0) {
440
+ const { ensureDaemonRunning, sendStreamingCommand } = yield Promise.resolve().then(() => __importStar(require("../lib/utils/daemon-client")));
441
+ if (!(yield ensureDaemonRunning())) {
442
+ if (!opts.agent)
443
+ console.log("WARN Stale chunker: daemon not running — start it (gmax watch --daemon -b) or run 'gmax index --reset' per project");
444
+ }
445
+ else {
446
+ for (const p of staleChunkerProjects) {
447
+ const name = p.name || path.basename(p.root);
448
+ if (!opts.agent)
449
+ console.log(`... Reindexing ${name} (--reset)...`);
450
+ const done = yield sendStreamingCommand({ cmd: "index", root: p.root, reset: true }, () => { }).catch((e) => ({ ok: false, error: String(e) }));
451
+ if (done.ok) {
452
+ (0, project_registry_1.registerProject)(Object.assign(Object.assign({}, p), { status: "indexed", chunkerVersion: config_1.CONFIG.CHUNKER_VERSION, lastIndexed: new Date().toISOString(), chunkCount: (_e = done.indexed) !== null && _e !== void 0 ? _e : p.chunkCount }));
453
+ const chunks = (_f = done.indexed) !== null && _f !== void 0 ? _f : 0;
454
+ if (opts.agent) {
455
+ console.log(`stale_chunker_reindexed\tname=${name}\tchunks=${chunks}`);
456
+ }
457
+ else {
458
+ console.log(`ok ${name} reindexed (${chunks} chunks)`);
459
+ }
460
+ fixed++;
461
+ }
462
+ else if (!opts.agent) {
463
+ console.log(`FAIL ${name}: reindex failed (${done.error})`);
464
+ }
465
+ }
466
+ }
467
+ }
375
468
  if (fixed === 0) {
376
469
  if (!opts.agent)
377
470
  console.log("ok Nothing to fix");
@@ -51,6 +51,7 @@ const vector_db_1 = require("../lib/store/vector-db");
51
51
  const agent_errors_1 = require("../lib/utils/agent-errors");
52
52
  const exit_1 = require("../lib/utils/exit");
53
53
  const project_registry_1 = require("../lib/utils/project-registry");
54
+ const stale_hint_1 = require("../lib/utils/stale-hint");
54
55
  const project_root_1 = require("../lib/utils/project-root");
55
56
  exports.impact = new commander_1.Command("impact")
56
57
  .description("Analyze change impact: dependents and affected tests")
@@ -72,6 +73,8 @@ exports.impact = new commander_1.Command("impact")
72
73
  if (root === null)
73
74
  return;
74
75
  const projectRoot = (_a = (0, project_root_1.findProjectRoot)(root)) !== null && _a !== void 0 ? _a : root;
76
+ (0, stale_hint_1.maybeWarnStaleChunker)(projectRoot, { agent: opts.agent });
77
+ (0, stale_hint_1.maybeWarnStaleEmbedding)(projectRoot, { agent: opts.agent });
75
78
  const paths = (0, project_root_1.ensureProjectPaths)(projectRoot);
76
79
  vectorDb = new vector_db_1.VectorDB(paths.lancedbDir);
77
80
  const { symbols, resolvedAsFile } = yield (0, impact_1.resolveTargetSymbols)(target, vectorDb, projectRoot);
@@ -53,6 +53,7 @@ const exit_1 = require("../lib/utils/exit");
53
53
  const filter_builder_1 = require("../lib/utils/filter-builder");
54
54
  const language_1 = require("../lib/utils/language");
55
55
  const project_registry_1 = require("../lib/utils/project-registry");
56
+ const stale_hint_1 = require("../lib/utils/stale-hint");
56
57
  const project_root_1 = require("../lib/utils/project-root");
57
58
  const useColors = process.stdout.isTTY && !process.env.NO_COLOR;
58
59
  const style = {
@@ -129,6 +130,8 @@ exports.peek = new commander_1.Command("peek")
129
130
  const depth = Math.min(Math.max(Number.parseInt(opts.depth || "1", 10), 1), 3);
130
131
  try {
131
132
  const projectRoot = (_a = (0, project_root_1.findProjectRoot)(root)) !== null && _a !== void 0 ? _a : root;
133
+ (0, stale_hint_1.maybeWarnStaleChunker)(projectRoot, { agent: opts.agent });
134
+ (0, stale_hint_1.maybeWarnStaleEmbedding)(projectRoot, { agent: opts.agent });
132
135
  const paths = (0, project_root_1.ensureProjectPaths)(projectRoot);
133
136
  vectorDb = new vector_db_1.VectorDB(paths.lancedbDir);
134
137
  const { resolveScope, buildScopeWhere } = yield Promise.resolve().then(() => __importStar(require("../lib/utils/scope-filter")));
@@ -52,6 +52,7 @@ const exit_1 = require("../lib/utils/exit");
52
52
  const project_registry_1 = require("../lib/utils/project-registry");
53
53
  const project_root_1 = require("../lib/utils/project-root");
54
54
  const query_timeout_1 = require("../lib/utils/query-timeout");
55
+ const stale_hint_1 = require("../lib/utils/stale-hint");
55
56
  const arrow_1 = require("../lib/utils/arrow");
56
57
  exports.related = new commander_1.Command("related")
57
58
  .description("Find files related by shared symbol references")
@@ -70,6 +71,8 @@ exports.related = new commander_1.Command("related")
70
71
  if (root === null)
71
72
  return;
72
73
  const projectRoot = (_a = (0, project_root_1.findProjectRoot)(root)) !== null && _a !== void 0 ? _a : root;
74
+ (0, stale_hint_1.maybeWarnStaleChunker)(projectRoot, { agent: opts.agent });
75
+ (0, stale_hint_1.maybeWarnStaleEmbedding)(projectRoot, { agent: opts.agent });
73
76
  const paths = (0, project_root_1.ensureProjectPaths)(projectRoot);
74
77
  vectorDb = new vector_db_1.VectorDB(paths.lancedbDir);
75
78
  const absPath = path.resolve(projectRoot, file);