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.
- package/LICENSE +202 -0
- package/NOTICE +33 -0
- package/README.md +375 -0
- package/dist/commands/claude-code.js +60 -0
- package/dist/commands/codex.js +98 -0
- package/dist/commands/doctor.js +92 -0
- package/dist/commands/droid.js +189 -0
- package/dist/commands/index.js +125 -0
- package/dist/commands/list.js +120 -0
- package/dist/commands/mcp.js +572 -0
- package/dist/commands/opencode.js +199 -0
- package/dist/commands/search.js +539 -0
- package/dist/commands/serve.js +512 -0
- package/dist/commands/setup.js +162 -0
- package/dist/commands/skeleton.js +288 -0
- package/dist/commands/symbols.js +129 -0
- package/dist/commands/trace.js +50 -0
- package/dist/commands/verify.js +174 -0
- package/dist/config.js +120 -0
- package/dist/eval.js +618 -0
- package/dist/index.js +82 -0
- package/dist/lib/core/languages.js +237 -0
- package/dist/lib/graph/graph-builder.js +105 -0
- package/dist/lib/index/chunker.js +663 -0
- package/dist/lib/index/grammar-loader.js +110 -0
- package/dist/lib/index/ignore-patterns.js +63 -0
- package/dist/lib/index/index-config.js +86 -0
- package/dist/lib/index/sync-helpers.js +97 -0
- package/dist/lib/index/syncer.js +396 -0
- package/dist/lib/index/walker.js +164 -0
- package/dist/lib/index/watcher.js +245 -0
- package/dist/lib/output/formatter.js +161 -0
- package/dist/lib/output/json-formatter.js +6 -0
- package/dist/lib/search/intent.js +23 -0
- package/dist/lib/search/searcher.js +475 -0
- package/dist/lib/setup/model-loader.js +107 -0
- package/dist/lib/setup/setup-helpers.js +106 -0
- package/dist/lib/skeleton/body-fields.js +175 -0
- package/dist/lib/skeleton/index.js +24 -0
- package/dist/lib/skeleton/retriever.js +36 -0
- package/dist/lib/skeleton/skeletonizer.js +483 -0
- package/dist/lib/skeleton/summary-formatter.js +90 -0
- package/dist/lib/store/meta-cache.js +143 -0
- package/dist/lib/store/types.js +2 -0
- package/dist/lib/store/vector-db.js +340 -0
- package/dist/lib/utils/cleanup.js +33 -0
- package/dist/lib/utils/exit.js +38 -0
- package/dist/lib/utils/file-utils.js +131 -0
- package/dist/lib/utils/filter-builder.js +17 -0
- package/dist/lib/utils/formatter.js +230 -0
- package/dist/lib/utils/git.js +83 -0
- package/dist/lib/utils/lock.js +157 -0
- package/dist/lib/utils/project-root.js +107 -0
- package/dist/lib/utils/server-registry.js +97 -0
- package/dist/lib/workers/colbert-math.js +107 -0
- package/dist/lib/workers/colbert-tokenizer.js +113 -0
- package/dist/lib/workers/download-worker.js +169 -0
- package/dist/lib/workers/embeddings/colbert.js +213 -0
- package/dist/lib/workers/embeddings/granite.js +180 -0
- package/dist/lib/workers/embeddings/mlx-client.js +144 -0
- package/dist/lib/workers/orchestrator.js +350 -0
- package/dist/lib/workers/pool.js +373 -0
- package/dist/lib/workers/process-child.js +92 -0
- package/dist/lib/workers/worker.js +31 -0
- package/package.json +80 -0
- package/plugins/osgrep/.claude-plugin/plugin.json +20 -0
- package/plugins/osgrep/hooks/start.js +92 -0
- package/plugins/osgrep/hooks/stop.js +3 -0
- package/plugins/osgrep/hooks.json +26 -0
- 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
|
+
}));
|