grepmax 0.17.21 → 0.17.23

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.
@@ -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 query_timeout_1 = require("../lib/utils/query-timeout");
54
55
  const arrow_1 = require("../lib/utils/arrow");
55
56
  exports.related = new commander_1.Command("related")
56
57
  .description("Find files related by shared symbol references")
@@ -164,12 +165,13 @@ exports.related = new commander_1.Command("related")
164
165
  basenameRejected = true;
165
166
  }
166
167
  else {
167
- const rows = yield table
168
+ // No .limit() here: LIKE + limit deadlocks in @lancedb 0.27.x when
169
+ // more rows match than the limit (verified). The loop below caps.
170
+ const rows = yield (0, query_timeout_1.withQueryTimeout)(table
168
171
  .query()
169
172
  .select(["path"])
170
173
  .where(`content LIKE '%${(0, filter_builder_1.escapeSqlString)(basename)}%' AND ${pathScope}`)
171
- .limit(limit * 4)
172
- .toArray();
174
+ .toArray(), `content LIKE %${basename}% (related mentions)`);
173
175
  const seen = new Set();
174
176
  for (const row of rows) {
175
177
  const p = String(row.path || "");
@@ -0,0 +1,227 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
36
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
37
+ return new (P || (P = Promise))(function (resolve, reject) {
38
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
39
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
40
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
41
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
42
+ });
43
+ };
44
+ Object.defineProperty(exports, "__esModule", { value: true });
45
+ exports.runSearch = runSearch;
46
+ const path = __importStar(require("node:path"));
47
+ const grammar_loader_1 = require("../lib/index/grammar-loader");
48
+ const sync_helpers_1 = require("../lib/index/sync-helpers");
49
+ const syncer_1 = require("../lib/index/syncer");
50
+ const searcher_1 = require("../lib/search/searcher");
51
+ const vector_db_1 = require("../lib/store/vector-db");
52
+ const lock_1 = require("../lib/utils/lock");
53
+ /**
54
+ * Acquire search results, picking the path: daemon-mediated first (ships the
55
+ * query over IPC to the already-warm daemon), in-process fallback otherwise
56
+ * (opens VectorDB, ensures the index, runs Searcher). The presentation/render
57
+ * stage stays in the command action.
58
+ *
59
+ * On success/dry-run the opened VectorDB (if any) is returned for the caller to
60
+ * close after rendering; if indexing throws, it is closed here before rethrow.
61
+ */
62
+ function runSearch(params) {
63
+ return __awaiter(this, void 0, void 0, function* () {
64
+ var _a, _b;
65
+ const { pattern, options, projectRoot, effectiveRoot, paths, projectStatus, searchFilters, pathFilter, seeds, } = params;
66
+ // Tracks a DB opened by the in-process path so it can be (a) returned to the
67
+ // caller for closing after render, or (b) closed here if indexing throws.
68
+ let openedDb = null;
69
+ try {
70
+ // Daemon-mediated search: ships query+args over IPC, daemon runs the
71
+ // hybrid+rerank against its already-warm VectorDB and worker pool.
72
+ // Drops cold-start cost (~17s wall, 6GB RAM in the CLI) to <1s. Falls
73
+ // back to in-process on any failure.
74
+ let searchResult = null;
75
+ let precomputedSkeletons;
76
+ let precomputedGraph;
77
+ let indexState;
78
+ if (!options.sync && !options.dryRun) {
79
+ try {
80
+ const { isDaemonRunning, sendDaemonCommand } = yield Promise.resolve().then(() => __importStar(require("../lib/utils/daemon-client")));
81
+ if (yield isDaemonRunning()) {
82
+ const resp = yield sendDaemonCommand({
83
+ cmd: "search",
84
+ projectRoot: effectiveRoot,
85
+ query: pattern,
86
+ limit: parseInt(options.m, 10),
87
+ filters: Object.keys(searchFilters).length > 0
88
+ ? searchFilters
89
+ : undefined,
90
+ pathPrefix: pathFilter,
91
+ rerank: process.env.GMAX_RERANK === "1",
92
+ explain: options.explain,
93
+ seeds,
94
+ includeSkeletons: options.skeleton,
95
+ includeGraph: options.symbol,
96
+ }, { timeoutMs: 60000 });
97
+ if (resp.ok) {
98
+ searchResult = {
99
+ data: resp.data,
100
+ warnings: resp.warnings,
101
+ };
102
+ precomputedSkeletons = resp.skeletons;
103
+ precomputedGraph = resp.graph;
104
+ indexState = resp.indexState;
105
+ }
106
+ else if (process.env.GMAX_DEBUG === "1") {
107
+ console.error(`[search] daemon path unavailable: ${(_a = resp.error) !== null && _a !== void 0 ? _a : "unknown"}`);
108
+ }
109
+ }
110
+ }
111
+ catch (err) {
112
+ if (process.env.GMAX_DEBUG === "1") {
113
+ console.error("[search] daemon attempt threw:", err);
114
+ }
115
+ }
116
+ }
117
+ // In-process fallback: open VectorDB, ensure index, run Searcher.
118
+ // Only entered when the daemon path didn't produce results.
119
+ if (!searchResult) {
120
+ const vectorDb = new vector_db_1.VectorDB(paths.lancedbDir);
121
+ openedDb = vectorDb;
122
+ // Check for active indexing lock and warn if present
123
+ const locked = (0, lock_1.isLocked)(paths.dataDir);
124
+ if (!options.agent && locked) {
125
+ console.warn("⚠️ Warning: Indexing in progress... search results may be incomplete.");
126
+ }
127
+ // No daemon here, so no precise pending count — surface the coarse
128
+ // signal (active lock or initial index not yet complete) so agent mode
129
+ // still gets a partial-index footer.
130
+ if (!indexState && (locked || projectStatus === "pending")) {
131
+ indexState = { indexing: true, pendingFiles: 0 };
132
+ }
133
+ const hasRows = yield vectorDb.hasAnyRows();
134
+ const needsSync = options.sync || !hasRows;
135
+ if (needsSync) {
136
+ const isTTY = process.stdout.isTTY;
137
+ let abortController;
138
+ let signal;
139
+ if (!isTTY) {
140
+ abortController = new AbortController();
141
+ signal = abortController.signal;
142
+ setTimeout(() => {
143
+ abortController === null || abortController === void 0 ? void 0 : abortController.abort();
144
+ }, 60000); // 60 seconds timeout for non-TTY auto-indexing
145
+ }
146
+ const { spinner, onProgress } = (0, sync_helpers_1.createIndexingSpinner)(projectRoot, options.sync ? "Indexing..." : "Indexing repository (first run)...");
147
+ try {
148
+ yield (0, grammar_loader_1.ensureGrammars)(console.log, { silent: true });
149
+ const result = yield (0, syncer_1.initialSync)({
150
+ projectRoot,
151
+ dryRun: options.dryRun,
152
+ onProgress,
153
+ signal,
154
+ });
155
+ if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
156
+ spinner.warn(`Indexing timed out (${result.processed}/${result.total}). Results may be partial.`);
157
+ }
158
+ if (options.dryRun) {
159
+ spinner.succeed(`Dry run complete (${result.processed}/${result.total}) • would have indexed ${result.indexed}`);
160
+ console.log((0, sync_helpers_1.formatDryRunSummary)(result, {
161
+ actionDescription: "would have indexed",
162
+ includeTotal: true,
163
+ }));
164
+ return { kind: "dry-run", vectorDb: openedDb };
165
+ }
166
+ yield vectorDb.createFTSIndex();
167
+ // Update registry after sync
168
+ const { readGlobalConfig } = yield Promise.resolve().then(() => __importStar(require("../lib/index/index-config")));
169
+ const { registerProject } = yield Promise.resolve().then(() => __importStar(require("../lib/utils/project-registry")));
170
+ const gc = readGlobalConfig();
171
+ registerProject({
172
+ root: projectRoot,
173
+ name: path.basename(projectRoot),
174
+ vectorDim: gc.vectorDim,
175
+ modelTier: gc.modelTier,
176
+ embedMode: gc.embedMode,
177
+ lastIndexed: new Date().toISOString(),
178
+ chunkCount: result.indexed,
179
+ status: "indexed",
180
+ });
181
+ const failedSuffix = result.failedFiles > 0 ? ` • ${result.failedFiles} failed` : "";
182
+ spinner.succeed(`${options.sync ? "Indexing" : "Initial indexing"} complete (${result.processed}/${result.total}) • indexed ${result.indexed}${failedSuffix}`);
183
+ }
184
+ catch (e) {
185
+ spinner.fail("Indexing failed");
186
+ throw e;
187
+ }
188
+ }
189
+ // Ensure a watcher is running for live reindexing
190
+ if (!process.env.VITEST && !((_b = process.env.NODE_ENV) === null || _b === void 0 ? void 0 : _b.includes("test"))) {
191
+ const { launchWatcher } = yield Promise.resolve().then(() => __importStar(require("../lib/utils/watcher-launcher")));
192
+ const launched = yield launchWatcher(projectRoot);
193
+ if (!launched.ok && launched.reason === "spawn-failed") {
194
+ console.warn(`[search] ${launched.message}`);
195
+ }
196
+ }
197
+ const searcher = new searcher_1.Searcher(vectorDb);
198
+ searchResult = yield searcher.search(pattern, parseInt(options.m, 10), {
199
+ rerank: process.env.GMAX_RERANK === "1",
200
+ explain: options.explain,
201
+ seeds,
202
+ }, Object.keys(searchFilters).length > 0
203
+ ? searchFilters
204
+ : undefined, pathFilter);
205
+ } // end if (!searchResult) — in-process fallback
206
+ return {
207
+ kind: "result",
208
+ vectorDb: openedDb,
209
+ searchResult,
210
+ precomputedSkeletons,
211
+ precomputedGraph,
212
+ indexState,
213
+ };
214
+ }
215
+ catch (e) {
216
+ // Mirror the original action's finally: close a DB opened mid-flight when
217
+ // indexing throws, before the error propagates to the caller.
218
+ if (openedDb) {
219
+ try {
220
+ yield openedDb.close();
221
+ }
222
+ catch (_c) { }
223
+ }
224
+ throw e;
225
+ }
226
+ });
227
+ }
@@ -0,0 +1,133 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
36
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
37
+ return new (P || (P = Promise))(function (resolve, reject) {
38
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
39
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
40
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
41
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
42
+ });
43
+ };
44
+ Object.defineProperty(exports, "__esModule", { value: true });
45
+ exports.outputSkeletons = outputSkeletons;
46
+ const fs = __importStar(require("node:fs"));
47
+ const path = __importStar(require("node:path"));
48
+ const skeleton_1 = require("../lib/skeleton");
49
+ const retriever_1 = require("../lib/skeleton/retriever");
50
+ // Reuse Skeletonizer instance
51
+ let globalSkeletonizer = null;
52
+ function outputSkeletons(results, projectRoot, limit, db, precomputed) {
53
+ return __awaiter(this, void 0, void 0, function* () {
54
+ var _a, _b;
55
+ const seenPaths = new Set();
56
+ const filesToProcess = [];
57
+ for (const result of results) {
58
+ const p = (_a = result.metadata) === null || _a === void 0 ? void 0 : _a.path;
59
+ if (typeof p === "string" && !seenPaths.has(p)) {
60
+ seenPaths.add(p);
61
+ filesToProcess.push(p);
62
+ if (filesToProcess.length >= limit)
63
+ break;
64
+ }
65
+ }
66
+ if (filesToProcess.length === 0) {
67
+ console.log("No skeleton matches found.");
68
+ console.log("\nTry: broaden your query, or use `gmax skeleton <path>` to view a specific file's structure.");
69
+ process.exitCode = 1;
70
+ return;
71
+ }
72
+ // Reuse or init skeletonizer for fallbacks
73
+ if (!globalSkeletonizer) {
74
+ globalSkeletonizer = new skeleton_1.Skeletonizer();
75
+ // Lazy init only if we actually fallback
76
+ }
77
+ const skeletonOpts = { includeSummary: true };
78
+ const skeletonResults = [];
79
+ for (const filePath of filesToProcess) {
80
+ // Paths from search results are now absolute (centralized index)
81
+ const absPath = path.isAbsolute(filePath)
82
+ ? filePath
83
+ : path.resolve(projectRoot, filePath);
84
+ // 0. Daemon-supplied (preferred — already-warm DB lookup, no cold open)
85
+ const fromDaemon = (_b = precomputed === null || precomputed === void 0 ? void 0 : precomputed[absPath]) !== null && _b !== void 0 ? _b : precomputed === null || precomputed === void 0 ? void 0 : precomputed[filePath];
86
+ if (fromDaemon) {
87
+ skeletonResults.push({
88
+ file: filePath,
89
+ skeleton: fromDaemon,
90
+ tokens: Math.ceil(fromDaemon.length / 4),
91
+ });
92
+ continue;
93
+ }
94
+ // 1. Try DB cache
95
+ if (db) {
96
+ const cached = yield (0, retriever_1.getStoredSkeleton)(db, absPath);
97
+ if (cached) {
98
+ skeletonResults.push({
99
+ file: filePath,
100
+ skeleton: cached,
101
+ tokens: Math.ceil(cached.length / 4), // Rough estimate
102
+ });
103
+ continue;
104
+ }
105
+ }
106
+ // 2. Fallback to fresh generation
107
+ yield globalSkeletonizer.init();
108
+ if (!fs.existsSync(absPath)) {
109
+ skeletonResults.push({
110
+ file: filePath,
111
+ skeleton: `// File not found: ${filePath}`,
112
+ tokens: 0,
113
+ error: "File not found",
114
+ });
115
+ continue;
116
+ }
117
+ const content = fs.readFileSync(absPath, "utf-8");
118
+ const res = yield globalSkeletonizer.skeletonizeFile(absPath, content, skeletonOpts);
119
+ skeletonResults.push({
120
+ file: filePath,
121
+ skeleton: res.skeleton,
122
+ tokens: res.tokenEstimate,
123
+ error: res.error,
124
+ });
125
+ }
126
+ // Since search doesn't support --json explicitly yet, we just print text.
127
+ // But if we ever add it, we have the structure.
128
+ for (const res of skeletonResults) {
129
+ console.log(res.skeleton);
130
+ console.log(""); // Separator
131
+ }
132
+ });
133
+ }