grepmax 0.7.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.
Files changed (70) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +33 -0
  3. package/README.md +375 -0
  4. package/dist/commands/claude-code.js +60 -0
  5. package/dist/commands/codex.js +98 -0
  6. package/dist/commands/doctor.js +92 -0
  7. package/dist/commands/droid.js +189 -0
  8. package/dist/commands/index.js +125 -0
  9. package/dist/commands/list.js +120 -0
  10. package/dist/commands/mcp.js +572 -0
  11. package/dist/commands/opencode.js +199 -0
  12. package/dist/commands/search.js +539 -0
  13. package/dist/commands/serve.js +512 -0
  14. package/dist/commands/setup.js +162 -0
  15. package/dist/commands/skeleton.js +288 -0
  16. package/dist/commands/symbols.js +129 -0
  17. package/dist/commands/trace.js +50 -0
  18. package/dist/commands/verify.js +174 -0
  19. package/dist/config.js +120 -0
  20. package/dist/eval.js +618 -0
  21. package/dist/index.js +82 -0
  22. package/dist/lib/core/languages.js +237 -0
  23. package/dist/lib/graph/graph-builder.js +105 -0
  24. package/dist/lib/index/chunker.js +663 -0
  25. package/dist/lib/index/grammar-loader.js +110 -0
  26. package/dist/lib/index/ignore-patterns.js +63 -0
  27. package/dist/lib/index/index-config.js +86 -0
  28. package/dist/lib/index/sync-helpers.js +97 -0
  29. package/dist/lib/index/syncer.js +396 -0
  30. package/dist/lib/index/walker.js +164 -0
  31. package/dist/lib/index/watcher.js +245 -0
  32. package/dist/lib/output/formatter.js +161 -0
  33. package/dist/lib/output/json-formatter.js +6 -0
  34. package/dist/lib/search/intent.js +23 -0
  35. package/dist/lib/search/searcher.js +475 -0
  36. package/dist/lib/setup/model-loader.js +107 -0
  37. package/dist/lib/setup/setup-helpers.js +106 -0
  38. package/dist/lib/skeleton/body-fields.js +175 -0
  39. package/dist/lib/skeleton/index.js +24 -0
  40. package/dist/lib/skeleton/retriever.js +36 -0
  41. package/dist/lib/skeleton/skeletonizer.js +483 -0
  42. package/dist/lib/skeleton/summary-formatter.js +90 -0
  43. package/dist/lib/store/meta-cache.js +143 -0
  44. package/dist/lib/store/types.js +2 -0
  45. package/dist/lib/store/vector-db.js +340 -0
  46. package/dist/lib/utils/cleanup.js +33 -0
  47. package/dist/lib/utils/exit.js +38 -0
  48. package/dist/lib/utils/file-utils.js +131 -0
  49. package/dist/lib/utils/filter-builder.js +17 -0
  50. package/dist/lib/utils/formatter.js +230 -0
  51. package/dist/lib/utils/git.js +83 -0
  52. package/dist/lib/utils/lock.js +157 -0
  53. package/dist/lib/utils/project-root.js +107 -0
  54. package/dist/lib/utils/server-registry.js +97 -0
  55. package/dist/lib/workers/colbert-math.js +107 -0
  56. package/dist/lib/workers/colbert-tokenizer.js +113 -0
  57. package/dist/lib/workers/download-worker.js +169 -0
  58. package/dist/lib/workers/embeddings/colbert.js +213 -0
  59. package/dist/lib/workers/embeddings/granite.js +180 -0
  60. package/dist/lib/workers/embeddings/mlx-client.js +144 -0
  61. package/dist/lib/workers/orchestrator.js +350 -0
  62. package/dist/lib/workers/pool.js +373 -0
  63. package/dist/lib/workers/process-child.js +92 -0
  64. package/dist/lib/workers/worker.js +31 -0
  65. package/package.json +80 -0
  66. package/plugins/osgrep/.claude-plugin/plugin.json +20 -0
  67. package/plugins/osgrep/hooks/start.js +92 -0
  68. package/plugins/osgrep/hooks/stop.js +3 -0
  69. package/plugins/osgrep/hooks.json +26 -0
  70. package/plugins/osgrep/skills/osgrep/SKILL.md +82 -0
@@ -0,0 +1,539 @@
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.search = void 0;
46
+ const fs = __importStar(require("node:fs"));
47
+ const path = __importStar(require("node:path"));
48
+ const commander_1 = require("commander");
49
+ const grammar_loader_1 = require("../lib/index/grammar-loader");
50
+ const sync_helpers_1 = require("../lib/index/sync-helpers");
51
+ const syncer_1 = require("../lib/index/syncer");
52
+ const searcher_1 = require("../lib/search/searcher");
53
+ const setup_helpers_1 = require("../lib/setup/setup-helpers");
54
+ const skeleton_1 = require("../lib/skeleton");
55
+ const retriever_1 = require("../lib/skeleton/retriever");
56
+ const vector_db_1 = require("../lib/store/vector-db");
57
+ const exit_1 = require("../lib/utils/exit");
58
+ const formatter_1 = require("../lib/utils/formatter");
59
+ const lock_1 = require("../lib/utils/lock");
60
+ const project_root_1 = require("../lib/utils/project-root");
61
+ const server_registry_1 = require("../lib/utils/server-registry");
62
+ function toTextResults(data) {
63
+ return data.map((r) => {
64
+ var _a, _b, _c, _d, _e, _f;
65
+ const rawPath = typeof ((_a = r.metadata) === null || _a === void 0 ? void 0 : _a.path) === "string"
66
+ ? r.metadata.path
67
+ : "Unknown path";
68
+ const start = typeof ((_b = r.generated_metadata) === null || _b === void 0 ? void 0 : _b.start_line) === "number"
69
+ ? r.generated_metadata.start_line
70
+ : 0;
71
+ const end = typeof ((_c = r.generated_metadata) === null || _c === void 0 ? void 0 : _c.end_line) === "number"
72
+ ? r.generated_metadata.end_line
73
+ : start + Math.max(0, ((_e = (_d = r.generated_metadata) === null || _d === void 0 ? void 0 : _d.num_lines) !== null && _e !== void 0 ? _e : 1) - 1);
74
+ return {
75
+ path: rawPath,
76
+ score: r.score,
77
+ content: r.text || "",
78
+ chunk_type: (_f = r.generated_metadata) === null || _f === void 0 ? void 0 : _f.type,
79
+ start_line: start,
80
+ end_line: end,
81
+ };
82
+ });
83
+ }
84
+ function getPreviewText(chunk) {
85
+ var _a, _b, _c, _d, _e;
86
+ const maxLen = 140;
87
+ const lines = (_b = (_a = chunk.text) === null || _a === void 0 ? void 0 : _a.split("\n").map((l) => l.trim()).filter(Boolean)) !== null && _b !== void 0 ? _b : [];
88
+ let preview = (_c = lines[0]) !== null && _c !== void 0 ? _c : "";
89
+ if (!preview && ((_d = chunk.defined_symbols) === null || _d === void 0 ? void 0 : _d.length)) {
90
+ preview = (_e = chunk.defined_symbols[0]) !== null && _e !== void 0 ? _e : "";
91
+ }
92
+ if (preview.length > maxLen) {
93
+ preview = `${preview.slice(0, maxLen)}...`;
94
+ }
95
+ return preview;
96
+ }
97
+ function toCompactHits(data) {
98
+ return data.map((chunk) => {
99
+ var _a, _b, _c, _d, _e, _f;
100
+ const rawPath = typeof ((_a = chunk.metadata) === null || _a === void 0 ? void 0 : _a.path) === "string"
101
+ ? chunk.metadata.path
102
+ : "Unknown path";
103
+ const start = typeof ((_b = chunk.generated_metadata) === null || _b === void 0 ? void 0 : _b.start_line) === "number"
104
+ ? chunk.generated_metadata.start_line
105
+ : 0;
106
+ const end = typeof ((_c = chunk.generated_metadata) === null || _c === void 0 ? void 0 : _c.end_line) === "number"
107
+ ? chunk.generated_metadata.end_line
108
+ : start + Math.max(0, ((_e = (_d = chunk.generated_metadata) === null || _d === void 0 ? void 0 : _d.num_lines) !== null && _e !== void 0 ? _e : 1) - 1);
109
+ return {
110
+ path: rawPath,
111
+ range: `${start + 1}-${end + 1}`,
112
+ start_line: start,
113
+ end_line: end,
114
+ role: chunk.role,
115
+ confidence: chunk.confidence,
116
+ score: chunk.score,
117
+ defined: Array.isArray(chunk.defined_symbols)
118
+ ? chunk.defined_symbols.slice(0, 3)
119
+ : typeof chunk.defined_symbols === "string"
120
+ ? [chunk.defined_symbols]
121
+ : typeof ((_f = chunk.defined_symbols) === null || _f === void 0 ? void 0 : _f.toArray) === "function"
122
+ ? chunk.defined_symbols.toArray().slice(0, 3)
123
+ : [],
124
+ preview: getPreviewText(chunk),
125
+ };
126
+ });
127
+ }
128
+ function compactRole(role) {
129
+ if (!role)
130
+ return "UNK";
131
+ if (role.startsWith("ORCH"))
132
+ return "ORCH";
133
+ if (role.startsWith("DEF"))
134
+ return "DEF";
135
+ if (role.startsWith("IMP"))
136
+ return "IMPL";
137
+ return role.slice(0, 4).toUpperCase();
138
+ }
139
+ function compactConf(conf) {
140
+ if (!conf)
141
+ return "U";
142
+ const c = conf.toUpperCase();
143
+ if (c.startsWith("H"))
144
+ return "H";
145
+ if (c.startsWith("M"))
146
+ return "M";
147
+ if (c.startsWith("L"))
148
+ return "L";
149
+ return "U";
150
+ }
151
+ function compactScore(score) {
152
+ if (typeof score !== "number")
153
+ return "";
154
+ const fixed = score.toFixed(3);
155
+ return fixed
156
+ .replace(/^0\./, ".")
157
+ .replace(/\.?0+$/, (m) => (m.startsWith(".") ? "" : m));
158
+ }
159
+ function truncateEnd(s, max) {
160
+ if (max <= 0)
161
+ return "";
162
+ if (s.length <= max)
163
+ return s;
164
+ if (max <= 3)
165
+ return s.slice(0, max);
166
+ return `${s.slice(0, max - 3)}...`;
167
+ }
168
+ function padR(s, w) {
169
+ const n = Math.max(0, w - s.length);
170
+ return s + " ".repeat(n);
171
+ }
172
+ function padL(s, w) {
173
+ const n = Math.max(0, w - s.length);
174
+ return " ".repeat(n) + s;
175
+ }
176
+ function formatCompactTSV(hits, projectRoot, query) {
177
+ var _a;
178
+ if (!hits.length)
179
+ return "No matches found.";
180
+ const lines = [];
181
+ lines.push(`osgrep hits\tquery=${query}\tcount=${hits.length}`);
182
+ lines.push("path\tlines\tscore\trole\tconf\tdefined");
183
+ for (const hit of hits) {
184
+ const relPath = path.isAbsolute(hit.path)
185
+ ? path.relative(projectRoot, hit.path)
186
+ : hit.path;
187
+ const score = compactScore(hit.score);
188
+ const role = compactRole(hit.role);
189
+ const conf = compactConf(hit.confidence);
190
+ const defs = ((_a = hit.defined) !== null && _a !== void 0 ? _a : []).join(",");
191
+ lines.push([relPath, hit.range, score, role, conf, defs].join("\t"));
192
+ }
193
+ return lines.join("\n");
194
+ }
195
+ function formatCompactPretty(hits, projectRoot, query, termWidth, useAnsi) {
196
+ var _a;
197
+ if (!hits.length)
198
+ return "No matches found.";
199
+ const dim = (s) => (useAnsi ? `\x1b[90m${s}\x1b[0m` : s);
200
+ const bold = (s) => (useAnsi ? `\x1b[1m${s}\x1b[0m` : s);
201
+ const wLines = 9;
202
+ const wScore = 6;
203
+ const wRole = 4;
204
+ const wConf = 1;
205
+ const wDef = 20;
206
+ const gutters = 5;
207
+ const fixed = wLines + wScore + wRole + wConf + wDef + gutters;
208
+ const wPath = Math.max(24, Math.min(64, termWidth - fixed));
209
+ const header = `osgrep hits count=${hits.length} query="${query}"`;
210
+ const cols = [
211
+ padR("path", wPath),
212
+ padR("lines", wLines),
213
+ padL("score", wScore),
214
+ padR("role", wRole),
215
+ padR("c", wConf),
216
+ padR("defined", wDef),
217
+ ].join(" ");
218
+ const out = [];
219
+ out.push(bold(header));
220
+ out.push(dim(cols));
221
+ for (const hit of hits) {
222
+ const relPath = path.isAbsolute(hit.path)
223
+ ? path.relative(projectRoot, hit.path)
224
+ : hit.path;
225
+ const score = compactScore(hit.score);
226
+ const role = compactRole(hit.role);
227
+ const conf = compactConf(hit.confidence);
228
+ const defs = ((_a = hit.defined) !== null && _a !== void 0 ? _a : []).join(",") || "-";
229
+ const displayPath = `${relPath}:${hit.start_line + 1}`;
230
+ const paddedPath = padR(displayPath, wPath);
231
+ const row = [
232
+ paddedPath,
233
+ padR(hit.range, wLines),
234
+ padL(score || "", wScore),
235
+ padR(role, wRole),
236
+ padR(conf, wConf),
237
+ padR(truncateEnd(defs, wDef), wDef),
238
+ ].join(" ");
239
+ out.push(row);
240
+ }
241
+ return out.join("\n");
242
+ }
243
+ function formatCompactTable(hits, projectRoot, query, opts) {
244
+ var _a;
245
+ if (!hits.length)
246
+ return "No matches found.";
247
+ if (!opts.isTTY || opts.plain) {
248
+ return formatCompactTSV(hits, projectRoot, query);
249
+ }
250
+ const termWidth = Math.max(80, (_a = process.stdout.columns) !== null && _a !== void 0 ? _a : 120);
251
+ return formatCompactPretty(hits, projectRoot, query, termWidth, true);
252
+ }
253
+ // Reuse Skeletonizer instance
254
+ let globalSkeletonizer = null;
255
+ function outputSkeletons(results, projectRoot, limit, db) {
256
+ return __awaiter(this, void 0, void 0, function* () {
257
+ var _a;
258
+ const seenPaths = new Set();
259
+ const filesToProcess = [];
260
+ for (const result of results) {
261
+ const p = (_a = result.metadata) === null || _a === void 0 ? void 0 : _a.path;
262
+ if (typeof p === "string" && !seenPaths.has(p)) {
263
+ seenPaths.add(p);
264
+ filesToProcess.push(p);
265
+ if (filesToProcess.length >= limit)
266
+ break;
267
+ }
268
+ }
269
+ if (filesToProcess.length === 0) {
270
+ console.log("No skeleton matches found.");
271
+ return;
272
+ }
273
+ // Reuse or init skeletonizer for fallbacks
274
+ if (!globalSkeletonizer) {
275
+ globalSkeletonizer = new skeleton_1.Skeletonizer();
276
+ // Lazy init only if we actually fallback
277
+ }
278
+ const skeletonOpts = { includeSummary: true };
279
+ const skeletonResults = [];
280
+ for (const relPath of filesToProcess) {
281
+ // 1. Try DB cache
282
+ if (db) {
283
+ const cached = yield (0, retriever_1.getStoredSkeleton)(db, relPath);
284
+ if (cached) {
285
+ skeletonResults.push({
286
+ file: relPath,
287
+ skeleton: cached,
288
+ tokens: Math.ceil(cached.length / 4), // Rough estimate
289
+ });
290
+ continue;
291
+ }
292
+ }
293
+ // 2. Fallback to fresh generation
294
+ yield globalSkeletonizer.init();
295
+ const absPath = path.resolve(projectRoot, relPath);
296
+ if (!fs.existsSync(absPath)) {
297
+ skeletonResults.push({
298
+ file: relPath,
299
+ skeleton: `// File not found: ${relPath}`,
300
+ tokens: 0,
301
+ error: "File not found",
302
+ });
303
+ continue;
304
+ }
305
+ const content = fs.readFileSync(absPath, "utf-8");
306
+ const res = yield globalSkeletonizer.skeletonizeFile(relPath, content, skeletonOpts);
307
+ skeletonResults.push({
308
+ file: relPath,
309
+ skeleton: res.skeleton,
310
+ tokens: res.tokenEstimate,
311
+ error: res.error,
312
+ });
313
+ }
314
+ // Since search doesn't support --json explicitly yet, we just print text.
315
+ // But if we ever add it, we have the structure.
316
+ for (const res of skeletonResults) {
317
+ console.log(res.skeleton);
318
+ console.log(""); // Separator
319
+ }
320
+ });
321
+ }
322
+ exports.search = new commander_1.Command("search")
323
+ .description("File pattern searcher")
324
+ .option("-m <max_count>, --max-count <max_count>", "The maximum number of results to return (total)", "5")
325
+ .option("-c, --content", "Show full chunk content instead of snippets", false)
326
+ .option("--per-file <n>", "Number of matches to show per file", "3")
327
+ .option("--scores", "Show relevance scores", false)
328
+ .option("--min-score <score>", "Minimum relevance score (0-1) to include in results", "0")
329
+ .option("--compact", "Compact hits view (paths + line ranges + role/preview)", false)
330
+ .option("--plain", "Disable ANSI colors and use simpler formatting", false)
331
+ .option("-s, --sync", "Syncs the local files to the store before searching", false)
332
+ .option("-d, --dry-run", "Show what would be indexed without actually indexing", false)
333
+ .option("--skeleton", "Show code skeleton for matching files instead of snippets", false)
334
+ .argument("<pattern>", "The pattern to search for")
335
+ .argument("[path]", "The path to search in")
336
+ .action((pattern, exec_path, _options, cmd) => __awaiter(void 0, void 0, void 0, function* () {
337
+ var _a, _b;
338
+ const options = cmd.optsWithGlobals();
339
+ const root = process.cwd();
340
+ const minScore = Number.isFinite(Number.parseFloat(options.minScore))
341
+ ? Number.parseFloat(options.minScore)
342
+ : 0;
343
+ let vectorDb = null;
344
+ // Check for running server
345
+ const execPathForServer = exec_path ? path.resolve(exec_path) : root;
346
+ const projectRootForServer = (_a = (0, project_root_1.findProjectRoot)(execPathForServer)) !== null && _a !== void 0 ? _a : execPathForServer;
347
+ const server = (0, server_registry_1.getServerForProject)(projectRootForServer);
348
+ if (server) {
349
+ try {
350
+ const response = yield fetch(`http://localhost:${server.port}/search`, {
351
+ method: "POST",
352
+ headers: { "Content-Type": "application/json" },
353
+ body: JSON.stringify({
354
+ query: pattern,
355
+ limit: parseInt(options.m, 10),
356
+ path: exec_path
357
+ ? path.relative(projectRootForServer, path.resolve(exec_path))
358
+ : undefined,
359
+ }),
360
+ });
361
+ if (response.ok) {
362
+ const body = (yield response.json());
363
+ const searchResult = { data: body.results };
364
+ const filteredData = searchResult.data.filter((r) => typeof r.score !== "number" || r.score >= minScore);
365
+ if (options.skeleton) {
366
+ yield outputSkeletons(filteredData, projectRootForServer, parseInt(options.m, 10),
367
+ // Server doesn't easily expose DB instance here in HTTP client mode,
368
+ // but we are in client. Wait, this text implies "Server Search" block.
369
+ // Client talks to server. The server returns JSON.
370
+ // We don't have DB access here.
371
+ // So we pass null, and it will fallback to generating local skeleton (if file exists locally).
372
+ // This is acceptable for Phase 3.
373
+ null);
374
+ return;
375
+ }
376
+ const compactHits = options.compact
377
+ ? toCompactHits(filteredData)
378
+ : [];
379
+ if (options.compact) {
380
+ const compactText = compactHits.length
381
+ ? formatCompactTable(compactHits, projectRootForServer, pattern, {
382
+ isTTY: !!process.stdout.isTTY,
383
+ plain: !!options.plain,
384
+ })
385
+ : "No matches found.";
386
+ console.log(compactText);
387
+ return; // EXIT
388
+ }
389
+ if (!filteredData.length) {
390
+ console.log("No matches found.");
391
+ return; // EXIT
392
+ }
393
+ const isTTY = process.stdout.isTTY;
394
+ const shouldBePlain = options.plain || !isTTY;
395
+ if (shouldBePlain) {
396
+ const mappedResults = toTextResults(filteredData);
397
+ const output = (0, formatter_1.formatTextResults)(mappedResults, pattern, projectRootForServer, {
398
+ isPlain: true,
399
+ compact: options.compact,
400
+ content: options.content,
401
+ perFile: parseInt(options.perFile, 10),
402
+ showScores: options.scores,
403
+ });
404
+ console.log(output);
405
+ }
406
+ else {
407
+ const { formatResults } = yield Promise.resolve().then(() => __importStar(require("../lib/output/formatter")));
408
+ const output = formatResults(filteredData, projectRootForServer, {
409
+ content: options.content,
410
+ });
411
+ console.log(output);
412
+ }
413
+ return; // EXIT successful server search
414
+ }
415
+ }
416
+ catch (e) {
417
+ if (process.env.DEBUG) {
418
+ console.error("[search] server request failed, falling back to local:", e);
419
+ }
420
+ }
421
+ }
422
+ try {
423
+ yield (0, setup_helpers_1.ensureSetup)();
424
+ const searchRoot = exec_path ? path.resolve(exec_path) : root;
425
+ const projectRoot = (_b = (0, project_root_1.findProjectRoot)(searchRoot)) !== null && _b !== void 0 ? _b : searchRoot;
426
+ const paths = (0, project_root_1.ensureProjectPaths)(projectRoot);
427
+ // Propagate project root to worker processes
428
+ process.env.OSGREP_PROJECT_ROOT = projectRoot;
429
+ vectorDb = new vector_db_1.VectorDB(paths.lancedbDir);
430
+ // Check for active indexing lock and warn if present
431
+ // This allows agents (via shim) to know results might be partial.
432
+ if ((0, lock_1.isLocked)(paths.osgrepDir)) {
433
+ console.warn("⚠️ Warning: Indexing in progress... search results may be incomplete.");
434
+ }
435
+ const hasRows = yield vectorDb.hasAnyRows();
436
+ const needsSync = options.sync || !hasRows;
437
+ if (needsSync) {
438
+ const isTTY = process.stdout.isTTY;
439
+ let abortController;
440
+ let signal;
441
+ if (!isTTY) {
442
+ abortController = new AbortController();
443
+ signal = abortController.signal;
444
+ setTimeout(() => {
445
+ abortController === null || abortController === void 0 ? void 0 : abortController.abort();
446
+ }, 60000); // 60 seconds timeout for non-TTY auto-indexing
447
+ }
448
+ const { spinner, onProgress } = (0, sync_helpers_1.createIndexingSpinner)(projectRoot, options.sync ? "Indexing..." : "Indexing repository (first run)...");
449
+ try {
450
+ yield (0, grammar_loader_1.ensureGrammars)(console.log, { silent: true });
451
+ const result = yield (0, syncer_1.initialSync)({
452
+ projectRoot,
453
+ dryRun: options.dryRun,
454
+ onProgress,
455
+ signal,
456
+ });
457
+ if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
458
+ spinner.warn(`Indexing timed out (${result.processed}/${result.total}). Results may be partial.`);
459
+ }
460
+ if (options.dryRun) {
461
+ spinner.succeed(`Dry run complete (${result.processed}/${result.total}) • would have indexed ${result.indexed}`);
462
+ console.log((0, sync_helpers_1.formatDryRunSummary)(result, {
463
+ actionDescription: "would have indexed",
464
+ includeTotal: true,
465
+ }));
466
+ return;
467
+ }
468
+ yield vectorDb.createFTSIndex();
469
+ const failedSuffix = result.failedFiles > 0 ? ` • ${result.failedFiles} failed` : "";
470
+ spinner.succeed(`${options.sync ? "Indexing" : "Initial indexing"} complete (${result.processed}/${result.total}) • indexed ${result.indexed}${failedSuffix}`);
471
+ }
472
+ catch (e) {
473
+ spinner.fail("Indexing failed");
474
+ throw e;
475
+ }
476
+ }
477
+ const searcher = new searcher_1.Searcher(vectorDb);
478
+ const searchResult = yield searcher.search(pattern, parseInt(options.m, 10), { rerank: true }, undefined, exec_path ? path.relative(projectRoot, path.resolve(exec_path)) : "");
479
+ const filteredData = searchResult.data.filter((r) => typeof r.score !== "number" || r.score >= minScore);
480
+ if (options.skeleton) {
481
+ yield outputSkeletons(filteredData, projectRoot, parseInt(options.m, 10), vectorDb);
482
+ return;
483
+ }
484
+ const compactHits = options.compact ? toCompactHits(filteredData) : [];
485
+ const compactText = options.compact && compactHits.length
486
+ ? formatCompactTable(compactHits, projectRoot, pattern, {
487
+ isTTY: !!process.stdout.isTTY,
488
+ plain: !!options.plain,
489
+ })
490
+ : options.compact
491
+ ? "No matches found."
492
+ : "";
493
+ if (!filteredData.length) {
494
+ console.log("No matches found.");
495
+ return;
496
+ }
497
+ if (options.compact) {
498
+ console.log(compactText);
499
+ return;
500
+ }
501
+ const isTTY = process.stdout.isTTY;
502
+ const shouldBePlain = options.plain || !isTTY;
503
+ if (shouldBePlain) {
504
+ const mappedResults = toTextResults(filteredData);
505
+ const output = (0, formatter_1.formatTextResults)(mappedResults, pattern, projectRoot, {
506
+ isPlain: true,
507
+ compact: options.compact,
508
+ content: options.content,
509
+ perFile: parseInt(options.perFile, 10),
510
+ showScores: options.scores,
511
+ });
512
+ console.log(output);
513
+ }
514
+ else {
515
+ // Use new holographic formatter for TTY
516
+ const { formatResults } = yield Promise.resolve().then(() => __importStar(require("../lib/output/formatter")));
517
+ const output = formatResults(filteredData, projectRoot, {
518
+ content: options.content,
519
+ });
520
+ console.log(output);
521
+ }
522
+ }
523
+ catch (error) {
524
+ const message = error instanceof Error ? error.message : "Unknown error";
525
+ console.error("Search failed:", message);
526
+ process.exitCode = 1;
527
+ }
528
+ finally {
529
+ if (vectorDb) {
530
+ try {
531
+ yield vectorDb.close();
532
+ }
533
+ catch (err) {
534
+ console.error("Failed to close VectorDB:", err);
535
+ }
536
+ }
537
+ yield (0, exit_1.gracefulExit)();
538
+ }
539
+ }));