grepmax 0.17.23 → 0.17.24

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,7 @@ 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 });
121
123
  const paths = (0, project_root_1.ensureProjectPaths)(projectRoot);
122
124
  vectorDb = new vector_db_1.VectorDB(paths.lancedbDir);
123
125
  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,7 +236,7 @@ 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" &&
@@ -259,10 +259,19 @@ exports.doctor = new commander_1.Command("doctor")
259
259
  `stale_chunker=${staleChunkerProjects.length}`,
260
260
  ];
261
261
  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(", ")}`);
262
+ for (const p of staleChunkerProjects) {
263
+ const gap = (0, config_1.describeChunkerGap)(p.chunkerVersion);
264
+ if (!gap)
265
+ continue;
266
+ console.log([
267
+ "stale_chunker_project",
268
+ `name=${p.name || path.basename(p.root)}`,
269
+ `indexed_v=${gap.fromVersion}`,
270
+ `current_v=${gap.toVersion}`,
271
+ `severity=${gap.severity}`,
272
+ `note=${gap.notes.join("; ")}`,
273
+ `fix=gmax index --reset (in ${p.root})`,
274
+ ].join("\t"));
266
275
  }
267
276
  }
268
277
  else {
@@ -307,11 +316,19 @@ exports.doctor = new commander_1.Command("doctor")
307
316
  }
308
317
  // Daemon
309
318
  console.log(`${daemonUp ? "ok" : "INFO"} Daemon: ${daemonUp ? "running" : "not running"}`);
310
- // Index built by an older chunker — graph metadata fixes need a reindex
319
+ // Index built by an older chunker — graph metadata fixes need a reindex.
320
+ // Severity (and the WARN/INFO label) is driven by CHUNKER_VERSION_HISTORY:
321
+ // a breaking gap means stale metadata is wrong, an additive gap only
322
+ // means newer edges are missing.
311
323
  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`);
324
+ const anyBreaking = staleChunkerProjects.some((p) => { var _a; return ((_a = (0, config_1.describeChunkerGap)(p.chunkerVersion)) === null || _a === void 0 ? void 0 : _a.severity) === "breaking"; });
325
+ 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
326
  for (const p of staleChunkerProjects) {
314
- console.log(` - ${p.name || path.basename(p.root)}: run 'gmax index --reset' in ${p.root}`);
327
+ const gap = (0, config_1.describeChunkerGap)(p.chunkerVersion);
328
+ if (!gap)
329
+ continue;
330
+ console.log(` - ${p.name || path.basename(p.root)} (v${gap.fromVersion}→v${gap.toVersion}, ${gap.severity}): ${gap.notes.join(" ")}`);
331
+ console.log(` run 'gmax index --reset' in ${p.root}`);
315
332
  }
316
333
  }
317
334
  // Projects
@@ -342,7 +359,7 @@ exports.doctor = new commander_1.Command("doctor")
342
359
  }
343
360
  yield mc.close();
344
361
  }
345
- catch (_f) { }
362
+ catch (_h) { }
346
363
  }
347
364
  }
348
365
  // --fix auto-remediation
@@ -372,6 +389,38 @@ exports.doctor = new commander_1.Command("doctor")
372
389
  console.log(`ok Removed ${orphanedProjects.length} orphaned project(s) from registry`);
373
390
  fixed++;
374
391
  }
392
+ // Reindex projects whose index predates the current chunker. This is a
393
+ // full `--reset` reindex per project (routed through the daemon), so it
394
+ // can be slow on large repos — unlike the cheap fixes above.
395
+ if (staleChunkerProjects.length > 0) {
396
+ const { ensureDaemonRunning, sendStreamingCommand } = yield Promise.resolve().then(() => __importStar(require("../lib/utils/daemon-client")));
397
+ if (!(yield ensureDaemonRunning())) {
398
+ if (!opts.agent)
399
+ console.log("WARN Stale chunker: daemon not running — start it (gmax watch --daemon -b) or run 'gmax index --reset' per project");
400
+ }
401
+ else {
402
+ for (const p of staleChunkerProjects) {
403
+ const name = p.name || path.basename(p.root);
404
+ if (!opts.agent)
405
+ console.log(`... Reindexing ${name} (--reset)...`);
406
+ const done = yield sendStreamingCommand({ cmd: "index", root: p.root, reset: true }, () => { }).catch((e) => ({ ok: false, error: String(e) }));
407
+ if (done.ok) {
408
+ (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 }));
409
+ const chunks = (_f = done.indexed) !== null && _f !== void 0 ? _f : 0;
410
+ if (opts.agent) {
411
+ console.log(`stale_chunker_reindexed\tname=${name}\tchunks=${chunks}`);
412
+ }
413
+ else {
414
+ console.log(`ok ${name} reindexed (${chunks} chunks)`);
415
+ }
416
+ fixed++;
417
+ }
418
+ else if (!opts.agent) {
419
+ console.log(`FAIL ${name}: reindex failed (${done.error})`);
420
+ }
421
+ }
422
+ }
423
+ }
375
424
  if (fixed === 0) {
376
425
  if (!opts.agent)
377
426
  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,7 @@ 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 });
75
77
  const paths = (0, project_root_1.ensureProjectPaths)(projectRoot);
76
78
  vectorDb = new vector_db_1.VectorDB(paths.lancedbDir);
77
79
  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,7 @@ 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 });
132
134
  const paths = (0, project_root_1.ensureProjectPaths)(projectRoot);
133
135
  vectorDb = new vector_db_1.VectorDB(paths.lancedbDir);
134
136
  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,7 @@ 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 });
73
75
  const paths = (0, project_root_1.ensureProjectPaths)(projectRoot);
74
76
  vectorDb = new vector_db_1.VectorDB(paths.lancedbDir);
75
77
  const absPath = path.resolve(projectRoot, file);
@@ -56,6 +56,7 @@ const import_extractor_1 = require("../lib/utils/import-extractor");
56
56
  const project_registry_1 = require("../lib/utils/project-registry");
57
57
  const project_root_1 = require("../lib/utils/project-root");
58
58
  const server_registry_1 = require("../lib/utils/server-registry");
59
+ const stale_hint_1 = require("../lib/utils/stale-hint");
59
60
  const search_run_1 = require("./search-run");
60
61
  const search_skeletons_1 = require("./search-skeletons");
61
62
  exports.search = new commander_1.Command("search")
@@ -285,6 +286,7 @@ Examples:
285
286
  if (project.status === "pending") {
286
287
  console.warn("This project is still being indexed. Results may be incomplete.\n");
287
288
  }
289
+ (0, stale_hint_1.maybeWarnStaleChunker)(checkRoot, { agent: options.agent });
288
290
  // Compute effective paths + filters early — both the daemon-mediated
289
291
  // and in-process search paths need them. Reuse the resolved checkRoot
290
292
  // so --root <name> only resolves once per invocation.
@@ -51,6 +51,7 @@ const filter_builder_1 = require("../lib/utils/filter-builder");
51
51
  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
+ const stale_hint_1 = require("../lib/utils/stale-hint");
54
55
  const arrow_1 = require("../lib/utils/arrow");
55
56
  exports.similar = new commander_1.Command("similar")
56
57
  .description("Find semantically similar code to a symbol or file")
@@ -71,6 +72,7 @@ exports.similar = new commander_1.Command("similar")
71
72
  if (root === null)
72
73
  return;
73
74
  const projectRoot = (_a = (0, project_root_1.findProjectRoot)(root)) !== null && _a !== void 0 ? _a : root;
75
+ (0, stale_hint_1.maybeWarnStaleChunker)(projectRoot, { agent: opts.agent });
74
76
  const paths = (0, project_root_1.ensureProjectPaths)(projectRoot);
75
77
  vectorDb = new vector_db_1.VectorDB(paths.lancedbDir);
76
78
  const table = yield vectorDb.ensureTable();
@@ -50,6 +50,7 @@ const vector_db_1 = require("../lib/store/vector-db");
50
50
  const exit_1 = require("../lib/utils/exit");
51
51
  const project_registry_1 = require("../lib/utils/project-registry");
52
52
  const project_root_1 = require("../lib/utils/project-root");
53
+ const stale_hint_1 = require("../lib/utils/stale-hint");
53
54
  exports.testFind = new commander_1.Command("test")
54
55
  .description("Find tests that exercise a symbol or file")
55
56
  .argument("<target>", "Symbol name or file path")
@@ -67,6 +68,7 @@ exports.testFind = new commander_1.Command("test")
67
68
  if (root === null)
68
69
  return;
69
70
  const projectRoot = (_a = (0, project_root_1.findProjectRoot)(root)) !== null && _a !== void 0 ? _a : root;
71
+ (0, stale_hint_1.maybeWarnStaleChunker)(projectRoot, { agent: opts.agent });
70
72
  const paths = (0, project_root_1.ensureProjectPaths)(projectRoot);
71
73
  vectorDb = new vector_db_1.VectorDB(paths.lancedbDir);
72
74
  const { symbols, resolvedAsFile } = yield (0, impact_1.resolveTargetSymbols)(target, vectorDb, projectRoot);
@@ -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
  const useColors = process.stdout.isTTY && !process.env.NO_COLOR;
56
57
  const dim = (s) => (useColors ? `\x1b[2m${s}\x1b[22m` : s);
@@ -183,6 +184,7 @@ exports.trace = new commander_1.Command("trace")
183
184
  let vectorDb = null;
184
185
  try {
185
186
  const projectRoot = (_a = (0, project_root_1.findProjectRoot)(root)) !== null && _a !== void 0 ? _a : root;
187
+ (0, stale_hint_1.maybeWarnStaleChunker)(projectRoot, { agent: opts.agent });
186
188
  const paths = (0, project_root_1.ensureProjectPaths)(projectRoot);
187
189
  vectorDb = new vector_db_1.VectorDB(paths.lancedbDir);
188
190
  const { resolveScope } = yield Promise.resolve().then(() => __importStar(require("../lib/utils/scope-filter")));
package/dist/config.js CHANGED
@@ -33,7 +33,8 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.INDEXABLE_EXTENSIONS = exports.FRAGMENT_COMPACT_THRESHOLD = exports.DISK_LOW_BYTES = exports.DISK_CRITICAL_BYTES = exports.MAX_FILE_SIZE_BYTES = exports.PATHS = exports.MAX_WORKER_MEMORY_MB = exports.WORKER_BOOT_TIMEOUT_MS = exports.WORKER_TIMEOUT_MS = exports.CONFIG = exports.MODEL_IDS = exports.DEFAULT_MODEL_TIER = exports.MODEL_TIERS = void 0;
36
+ exports.INDEXABLE_EXTENSIONS = exports.FRAGMENT_COMPACT_THRESHOLD = exports.DISK_LOW_BYTES = exports.DISK_CRITICAL_BYTES = exports.MAX_FILE_SIZE_BYTES = exports.PATHS = exports.MAX_WORKER_MEMORY_MB = exports.WORKER_BOOT_TIMEOUT_MS = exports.WORKER_TIMEOUT_MS = exports.CHUNKER_VERSION_HISTORY = exports.CONFIG = exports.MODEL_IDS = exports.DEFAULT_MODEL_TIER = exports.MODEL_TIERS = void 0;
37
+ exports.describeChunkerGap = describeChunkerGap;
37
38
  const os = __importStar(require("node:os"));
38
39
  const path = __importStar(require("node:path"));
39
40
  exports.MODEL_TIERS = {
@@ -77,11 +78,51 @@ exports.CONFIG = {
77
78
  WORKER_THREADS: DEFAULT_WORKER_THREADS,
78
79
  QUERY_PREFIX: "",
79
80
  // Bump when chunk metadata semantics change in a way that requires a full
80
- // reindex to take effect. v2: sub-chunk symbol lists scoped to the chunk's
81
- // own content (previously every split sub-chunk inherited the parent's
82
- // full defined/referenced symbol lists, fabricating graph edges).
83
- CHUNKER_VERSION: 2,
81
+ // reindex to take effect. Must equal the latest entry's `v` in
82
+ // CHUNKER_VERSION_HISTORY below see that list for per-version severity and
83
+ // the user-facing note rendered by `gmax doctor` and the staleness hint.
84
+ CHUNKER_VERSION: 3,
84
85
  };
86
+ /**
87
+ * Per-version record of what changed in the chunker and how much it matters to
88
+ * an already-built index. `severity` drives tone: an "additive" change only
89
+ * adds new metadata (older indexes under-cover but aren't wrong), while a
90
+ * "breaking" change means older indexes carry incorrect metadata until a
91
+ * reindex. The `note` is shown verbatim to the user for the versions their
92
+ * index is missing. CONFIG.CHUNKER_VERSION must equal the highest `v` here.
93
+ */
94
+ exports.CHUNKER_VERSION_HISTORY = [
95
+ {
96
+ v: 2,
97
+ severity: "breaking",
98
+ note: "sub-chunk symbol scoping; graph overcounted callers before this.",
99
+ },
100
+ {
101
+ v: 3,
102
+ severity: "additive",
103
+ note: "type-position edges; dead/trace miss type-only callers until reindex.",
104
+ },
105
+ ];
106
+ /**
107
+ * Describe the gap between an index's stamped chunker version and the current
108
+ * one, or null when the index is already current. Shared by `gmax doctor` and
109
+ * the query-time staleness hint so both render the same severity + notes.
110
+ */
111
+ function describeChunkerGap(indexedVersion) {
112
+ const fromVersion = indexedVersion !== null && indexedVersion !== void 0 ? indexedVersion : 1;
113
+ if (fromVersion >= exports.CONFIG.CHUNKER_VERSION)
114
+ return null;
115
+ const missed = exports.CHUNKER_VERSION_HISTORY.filter((h) => h.v > fromVersion && h.v <= exports.CONFIG.CHUNKER_VERSION);
116
+ const severity = missed.some((h) => h.severity === "breaking")
117
+ ? "breaking"
118
+ : "additive";
119
+ return {
120
+ fromVersion,
121
+ toVersion: exports.CONFIG.CHUNKER_VERSION,
122
+ severity,
123
+ notes: missed.map((h) => h.note),
124
+ };
125
+ }
85
126
  exports.WORKER_TIMEOUT_MS = Number.parseInt(process.env.GMAX_WORKER_TIMEOUT_MS || "60000", 10);
86
127
  exports.WORKER_BOOT_TIMEOUT_MS = Number.parseInt(process.env.GMAX_WORKER_BOOT_TIMEOUT_MS || "300000", 10);
87
128
  exports.MAX_WORKER_MEMORY_MB = (() => {