lmgrep 0.1.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/README.md +181 -0
- package/completions/_lmgrep +88 -0
- package/dist/chunker/context.d.ts +25 -0
- package/dist/chunker/context.js +204 -0
- package/dist/chunker/context.js.map +1 -0
- package/dist/chunker/index.d.ts +3 -0
- package/dist/chunker/index.js +145 -0
- package/dist/chunker/index.js.map +1 -0
- package/dist/chunker/languages.d.ts +15 -0
- package/dist/chunker/languages.js +251 -0
- package/dist/chunker/languages.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +69 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +2 -0
- package/dist/config.js +31 -0
- package/dist/config.js.map +1 -0
- package/dist/embedder.d.ts +3 -0
- package/dist/embedder.js +55 -0
- package/dist/embedder.js.map +1 -0
- package/dist/index-cmd.d.ts +9 -0
- package/dist/index-cmd.js +250 -0
- package/dist/index-cmd.js.map +1 -0
- package/dist/mcp.d.ts +2 -0
- package/dist/mcp.js +80 -0
- package/dist/mcp.js.map +1 -0
- package/dist/providers.d.ts +1 -0
- package/dist/providers.js +43 -0
- package/dist/providers.js.map +1 -0
- package/dist/repair-cmd.d.ts +5 -0
- package/dist/repair-cmd.js +112 -0
- package/dist/repair-cmd.js.map +1 -0
- package/dist/search-cmd.d.ts +10 -0
- package/dist/search-cmd.js +60 -0
- package/dist/search-cmd.js.map +1 -0
- package/dist/serve-cmd.d.ts +1 -0
- package/dist/serve-cmd.js +139 -0
- package/dist/serve-cmd.js.map +1 -0
- package/dist/status-cmd.d.ts +5 -0
- package/dist/status-cmd.js +119 -0
- package/dist/status-cmd.js.map +1 -0
- package/dist/store.d.ts +25 -0
- package/dist/store.js +207 -0
- package/dist/store.js.map +1 -0
- package/dist/types.d.ts +40 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/walker.d.ts +3 -0
- package/dist/walker.js +90 -0
- package/dist/walker.js.map +1 -0
- package/package.json +57 -0
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
import { chunkFile } from "./chunker/index.js";
|
|
2
|
+
import { loadConfig } from "./config.js";
|
|
3
|
+
import { embedTexts } from "./embedder.js";
|
|
4
|
+
import { createHash } from "node:crypto";
|
|
5
|
+
import { readFileSync, statSync } from "node:fs";
|
|
6
|
+
import { join } from "node:path";
|
|
7
|
+
import { deleteByFiles, getFileHashes, getIndexedHashes, resetIndex, upsertChunks, upsertFileHashes, } from "./store.js";
|
|
8
|
+
import { walkFiles } from "./walker.js";
|
|
9
|
+
function parseDuration(s) {
|
|
10
|
+
const match = s.match(/^(\d+)\s*(s|m|h|d)$/);
|
|
11
|
+
if (!match)
|
|
12
|
+
throw new Error(`Invalid duration "${s}". Use e.g. 10m, 2h, 1d`);
|
|
13
|
+
const n = Number.parseInt(match[1], 10);
|
|
14
|
+
const unit = match[2];
|
|
15
|
+
const multipliers = { s: 1000, m: 60_000, h: 3_600_000, d: 86_400_000 };
|
|
16
|
+
return n * multipliers[unit];
|
|
17
|
+
}
|
|
18
|
+
function log(msg) {
|
|
19
|
+
console.log(`[${new Date().toISOString()}] ${msg}`);
|
|
20
|
+
}
|
|
21
|
+
export async function indexCommand(cwd, opts) {
|
|
22
|
+
const config = loadConfig(cwd);
|
|
23
|
+
if (opts.reset) {
|
|
24
|
+
log("Resetting index...");
|
|
25
|
+
await resetIndex(cwd);
|
|
26
|
+
}
|
|
27
|
+
// Scan files
|
|
28
|
+
const allFiles = walkFiles(cwd);
|
|
29
|
+
// Filter by mtime if --since is provided
|
|
30
|
+
let files = allFiles;
|
|
31
|
+
if (opts.since) {
|
|
32
|
+
const cutoff = Date.now() - parseDuration(opts.since);
|
|
33
|
+
files = allFiles.filter((f) => {
|
|
34
|
+
try {
|
|
35
|
+
return statSync(join(cwd, f)).mtimeMs >= cutoff;
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
log(`Found ${files.length} files modified in the last ${opts.since} (out of ${allFiles.length})`);
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
log(`Found ${files.length} files`);
|
|
45
|
+
}
|
|
46
|
+
// Fast change detection via whole-file hash
|
|
47
|
+
const storedFileHashes = await getFileHashes(cwd);
|
|
48
|
+
const changedFiles = [];
|
|
49
|
+
const currentFileHashes = new Map();
|
|
50
|
+
for (const file of files) {
|
|
51
|
+
try {
|
|
52
|
+
const content = readFileSync(join(cwd, file));
|
|
53
|
+
const fileHash = createHash("sha256").update(content).digest("hex").slice(0, 16);
|
|
54
|
+
currentFileHashes.set(file, fileHash);
|
|
55
|
+
if (opts.force || storedFileHashes.get(file) !== fileHash) {
|
|
56
|
+
changedFiles.push(file);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
// skip unreadable files
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
log(`${changedFiles.length} files changed out of ${files.length}`);
|
|
64
|
+
if (changedFiles.length === 0) {
|
|
65
|
+
log("No changes detected. Index is up to date.");
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
// Chunk only changed files
|
|
69
|
+
const allChunks = [];
|
|
70
|
+
for (let i = 0; i < changedFiles.length; i++) {
|
|
71
|
+
const file = changedFiles[i];
|
|
72
|
+
try {
|
|
73
|
+
const chunks = await chunkFile(file, cwd);
|
|
74
|
+
allChunks.push(...chunks);
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
// skip files that fail to parse
|
|
78
|
+
}
|
|
79
|
+
if ((i + 1) % 1000 === 0 || i === changedFiles.length - 1) {
|
|
80
|
+
log(`Chunking: ${i + 1}/${changedFiles.length} files, ${allChunks.length} chunks so far`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if (allChunks.length === 0) {
|
|
84
|
+
log("No changes detected. Index is up to date.");
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
if (opts.dry) {
|
|
88
|
+
for (const f of changedFiles)
|
|
89
|
+
log(` ${f}`);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
// Delete old chunks for changed files so stale content doesn't linger
|
|
93
|
+
await deleteByFiles(changedFiles, cwd);
|
|
94
|
+
// Filter out chunks already indexed (by hash) — checked after delete
|
|
95
|
+
// so hashes from changed files don't cause false positives
|
|
96
|
+
const existingHashes = await getIndexedHashes(cwd);
|
|
97
|
+
let newChunks = allChunks.filter((c) => !existingHashes.has(c.hash));
|
|
98
|
+
const alreadyIndexed = allChunks.length - newChunks.length;
|
|
99
|
+
// Filter out chunks exceeding max token limit
|
|
100
|
+
let skippedOversize = 0;
|
|
101
|
+
if (config.maxTokens) {
|
|
102
|
+
const before = newChunks.length;
|
|
103
|
+
newChunks = newChunks.filter((c) => {
|
|
104
|
+
const estimatedTokens = Math.ceil((c.context.length + c.content.length) / 4);
|
|
105
|
+
return estimatedTokens <= config.maxTokens;
|
|
106
|
+
});
|
|
107
|
+
skippedOversize = before - newChunks.length;
|
|
108
|
+
}
|
|
109
|
+
log(`${changedFiles.length} files changed, ${allChunks.length} chunks total, ${newChunks.length} to embed (${alreadyIndexed} already indexed${skippedOversize > 0 ? `, ${skippedOversize} skipped as oversized` : ""})`);
|
|
110
|
+
if (newChunks.length === 0) {
|
|
111
|
+
// No new chunks to embed — safe to save all file hashes
|
|
112
|
+
const allChangedHashEntries = changedFiles
|
|
113
|
+
.filter((fp) => currentFileHashes.has(fp))
|
|
114
|
+
.map((fp) => ({ filePath: fp, fileHash: currentFileHashes.get(fp) }));
|
|
115
|
+
if (allChangedHashEntries.length > 0) {
|
|
116
|
+
await upsertFileHashes(allChangedHashEntries, cwd);
|
|
117
|
+
}
|
|
118
|
+
log("All chunks already indexed.");
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
// Embed and store in batches
|
|
122
|
+
const { succeeded, failed, failedFiles } = await embedAndStore(newChunks, config, cwd);
|
|
123
|
+
// Only save file hashes for files that had no embedding failures,
|
|
124
|
+
// so interrupted/failed files get retried on the next run.
|
|
125
|
+
const hashEntries = changedFiles
|
|
126
|
+
.filter((fp) => currentFileHashes.has(fp) && !failedFiles.has(fp))
|
|
127
|
+
.map((fp) => ({ filePath: fp, fileHash: currentFileHashes.get(fp) }));
|
|
128
|
+
if (hashEntries.length > 0) {
|
|
129
|
+
await upsertFileHashes(hashEntries, cwd);
|
|
130
|
+
}
|
|
131
|
+
log(`Done: ${succeeded} chunks indexed from ${changedFiles.length} files` +
|
|
132
|
+
(failed > 0 ? ` (${failed} failed)` : ""));
|
|
133
|
+
}
|
|
134
|
+
const BATCH_DELAY_MS = 200;
|
|
135
|
+
const MAX_CONSECUTIVE_FAILURES = 3;
|
|
136
|
+
const MAX_RELOADS = 10;
|
|
137
|
+
async function reloadModel(config) {
|
|
138
|
+
const modelId = config.model.split(":").slice(1).join(":");
|
|
139
|
+
const baseURL = config.baseURL ?? "http://localhost:1234";
|
|
140
|
+
const apiBase = baseURL.replace(/\/v1\/?$/, "");
|
|
141
|
+
try {
|
|
142
|
+
log(`Reloading model "${modelId}"...`);
|
|
143
|
+
await fetch(`${apiBase}/api/v1/models/unload`, {
|
|
144
|
+
method: "POST",
|
|
145
|
+
headers: { "Content-Type": "application/json" },
|
|
146
|
+
body: JSON.stringify({ instance_id: modelId }),
|
|
147
|
+
});
|
|
148
|
+
await new Promise((r) => setTimeout(r, 2000));
|
|
149
|
+
const res = await fetch(`${apiBase}/api/v1/models/load`, {
|
|
150
|
+
method: "POST",
|
|
151
|
+
headers: { "Content-Type": "application/json" },
|
|
152
|
+
body: JSON.stringify({ model: modelId, context_length: config.maxTokens ?? 8192 }),
|
|
153
|
+
});
|
|
154
|
+
const data = (await res.json());
|
|
155
|
+
if (data.status === "loaded") {
|
|
156
|
+
log("Model reloaded successfully");
|
|
157
|
+
return true;
|
|
158
|
+
}
|
|
159
|
+
log(`Model reload response: ${JSON.stringify(data)}`);
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
catch (err) {
|
|
163
|
+
log(`Failed to reload model: ${err instanceof Error ? err.message : err}`);
|
|
164
|
+
return false;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
async function embedAndStore(chunks, config, cwd) {
|
|
168
|
+
const batchSize = config.batchSize;
|
|
169
|
+
const total = chunks.length;
|
|
170
|
+
const totalBatches = Math.ceil(total / batchSize);
|
|
171
|
+
let succeeded = 0;
|
|
172
|
+
let failed = 0;
|
|
173
|
+
const failedFiles = new Set();
|
|
174
|
+
let consecutiveFailedBatches = 0;
|
|
175
|
+
let reloads = 0;
|
|
176
|
+
for (let i = 0; i < chunks.length; i += batchSize) {
|
|
177
|
+
const batchNum = Math.floor(i / batchSize) + 1;
|
|
178
|
+
const batchChunks = chunks.slice(i, i + batchSize);
|
|
179
|
+
const batchTexts = batchChunks.map((c) => `${c.context}\n${c.content}`);
|
|
180
|
+
const batchIndexed = [];
|
|
181
|
+
let batchFailed = 0;
|
|
182
|
+
try {
|
|
183
|
+
const vectors = await embedTexts(batchTexts, config);
|
|
184
|
+
for (let j = 0; j < batchChunks.length; j++) {
|
|
185
|
+
batchIndexed.push({ ...batchChunks[j], vector: vectors[j] });
|
|
186
|
+
}
|
|
187
|
+
succeeded += batchChunks.length;
|
|
188
|
+
consecutiveFailedBatches = 0;
|
|
189
|
+
}
|
|
190
|
+
catch {
|
|
191
|
+
// Batch failed — retry each item individually
|
|
192
|
+
for (let j = 0; j < batchChunks.length; j++) {
|
|
193
|
+
const tokenEstimate = Math.ceil(batchTexts[j].length / 4);
|
|
194
|
+
try {
|
|
195
|
+
const [vector] = await embedTexts([batchTexts[j]], config);
|
|
196
|
+
batchIndexed.push({ ...batchChunks[j], vector });
|
|
197
|
+
succeeded++;
|
|
198
|
+
}
|
|
199
|
+
catch (err) {
|
|
200
|
+
failed++;
|
|
201
|
+
batchFailed++;
|
|
202
|
+
failedFiles.add(batchChunks[j].filePath);
|
|
203
|
+
console.error(` ! Failed (~${tokenEstimate} tok): ${err instanceof Error ? err.message : err}`);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
if (batchFailed === batchChunks.length) {
|
|
207
|
+
consecutiveFailedBatches++;
|
|
208
|
+
}
|
|
209
|
+
else {
|
|
210
|
+
consecutiveFailedBatches = 0;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
// Store successful chunks from this batch immediately
|
|
214
|
+
if (batchIndexed.length > 0) {
|
|
215
|
+
await upsertChunks(batchIndexed, cwd);
|
|
216
|
+
}
|
|
217
|
+
log(`Batch ${batchNum}/${totalBatches}: ${succeeded} ok / ${failed} err / ${total} total`);
|
|
218
|
+
if (consecutiveFailedBatches >= MAX_CONSECUTIVE_FAILURES) {
|
|
219
|
+
if (reloads >= MAX_RELOADS) {
|
|
220
|
+
log(`Aborting: exhausted ${MAX_RELOADS} reload attempts.`);
|
|
221
|
+
// Mark all remaining unprocessed chunks' files as failed
|
|
222
|
+
for (let k = i + batchSize; k < chunks.length; k++) {
|
|
223
|
+
failedFiles.add(chunks[k].filePath);
|
|
224
|
+
}
|
|
225
|
+
break;
|
|
226
|
+
}
|
|
227
|
+
reloads++;
|
|
228
|
+
log(`${MAX_CONSECUTIVE_FAILURES} consecutive failures — reloading model (attempt ${reloads}/${MAX_RELOADS})...`);
|
|
229
|
+
const ok = await reloadModel(config);
|
|
230
|
+
if (!ok) {
|
|
231
|
+
log("Aborting: could not reload model.");
|
|
232
|
+
// Mark all remaining unprocessed chunks' files as failed
|
|
233
|
+
for (let k = i + batchSize; k < chunks.length; k++) {
|
|
234
|
+
failedFiles.add(chunks[k].filePath);
|
|
235
|
+
}
|
|
236
|
+
break;
|
|
237
|
+
}
|
|
238
|
+
consecutiveFailedBatches = 0;
|
|
239
|
+
// Retry the failed batches by rewinding
|
|
240
|
+
i -= MAX_CONSECUTIVE_FAILURES * batchSize;
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
// Cooldown between batches to avoid GPU overheating
|
|
244
|
+
if (i + batchSize < chunks.length) {
|
|
245
|
+
await new Promise((r) => setTimeout(r, BATCH_DELAY_MS));
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
return { succeeded, failed, failedFiles };
|
|
249
|
+
}
|
|
250
|
+
//# sourceMappingURL=index-cmd.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-cmd.js","sourceRoot":"","sources":["../src/index-cmd.ts"],"names":[],"mappings":"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"}
|
package/dist/mcp.d.ts
ADDED
package/dist/mcp.js
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { spawn } from "node:child_process";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
6
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
import { loadConfig } from "./config.js";
|
|
9
|
+
import { embedQuery } from "./embedder.js";
|
|
10
|
+
import { findIndexedAncestor, search } from "./store.js";
|
|
11
|
+
const cwd = process.cwd();
|
|
12
|
+
// Start the serve watcher as a detached background process
|
|
13
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
14
|
+
const cliPath = join(__dirname, "cli.js");
|
|
15
|
+
const child = spawn("node", [cliPath, "serve"], {
|
|
16
|
+
cwd,
|
|
17
|
+
stdio: "ignore",
|
|
18
|
+
detached: true,
|
|
19
|
+
});
|
|
20
|
+
child.unref();
|
|
21
|
+
const server = new McpServer({
|
|
22
|
+
name: "lmgrep",
|
|
23
|
+
version: "0.1.0",
|
|
24
|
+
});
|
|
25
|
+
server.tool("search", "Semantic code search across the indexed codebase. Returns relevant code chunks ranked by similarity to the query.", {
|
|
26
|
+
query: z.string().describe("Natural language search query"),
|
|
27
|
+
limit: z
|
|
28
|
+
.number()
|
|
29
|
+
.optional()
|
|
30
|
+
.default(10)
|
|
31
|
+
.describe("Maximum number of results to return"),
|
|
32
|
+
filePrefix: z
|
|
33
|
+
.string()
|
|
34
|
+
.optional()
|
|
35
|
+
.describe("Only search files matching this path prefix (e.g. 'backend' or 'src/components')"),
|
|
36
|
+
not: z
|
|
37
|
+
.string()
|
|
38
|
+
.optional()
|
|
39
|
+
.describe("Exclude results similar to this query"),
|
|
40
|
+
}, async ({ query, limit, filePrefix, not }) => {
|
|
41
|
+
let searchCwd = cwd;
|
|
42
|
+
let prefix = filePrefix;
|
|
43
|
+
const ancestor = findIndexedAncestor(cwd);
|
|
44
|
+
if (ancestor && ancestor.prefix) {
|
|
45
|
+
searchCwd = ancestor.root;
|
|
46
|
+
prefix = prefix
|
|
47
|
+
? `${ancestor.prefix}/${prefix}`
|
|
48
|
+
: ancestor.prefix;
|
|
49
|
+
}
|
|
50
|
+
const config = loadConfig(searchCwd);
|
|
51
|
+
let queryVector = await embedQuery(query, config);
|
|
52
|
+
if (not) {
|
|
53
|
+
const notVector = await embedQuery(not, config);
|
|
54
|
+
queryVector = queryVector.map((v, i) => v - notVector[i] * 0.5);
|
|
55
|
+
}
|
|
56
|
+
const results = await search(queryVector, searchCwd, limit, prefix);
|
|
57
|
+
if (results.length === 0) {
|
|
58
|
+
return {
|
|
59
|
+
content: [{ type: "text", text: "No results found." }],
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
const text = results
|
|
63
|
+
.map((r) => {
|
|
64
|
+
const header = `${r.filePath}:${r.startLine}-${r.endLine} [${r.type}] ${r.name}`;
|
|
65
|
+
return `${header}\n${r.context}\n\n${r.content}`;
|
|
66
|
+
})
|
|
67
|
+
.join("\n\n---\n\n");
|
|
68
|
+
return {
|
|
69
|
+
content: [{ type: "text", text }],
|
|
70
|
+
};
|
|
71
|
+
});
|
|
72
|
+
async function main() {
|
|
73
|
+
const transport = new StdioServerTransport();
|
|
74
|
+
await server.connect(transport);
|
|
75
|
+
}
|
|
76
|
+
main().catch((err) => {
|
|
77
|
+
console.error("lmgrep MCP server error:", err);
|
|
78
|
+
process.exit(1);
|
|
79
|
+
});
|
|
80
|
+
//# sourceMappingURL=mcp.js.map
|
package/dist/mcp.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp.js","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function importProvider(pkg: string): Promise<Record<string, unknown>>;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { execSync } from "node:child_process";
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
function getGlobalNodeModulesPaths() {
|
|
5
|
+
const paths = [];
|
|
6
|
+
// pnpm global
|
|
7
|
+
try {
|
|
8
|
+
const pnpmGlobal = execSync("pnpm root -g", { stdio: "pipe" })
|
|
9
|
+
.toString()
|
|
10
|
+
.trim();
|
|
11
|
+
if (pnpmGlobal)
|
|
12
|
+
paths.push(pnpmGlobal);
|
|
13
|
+
}
|
|
14
|
+
catch { }
|
|
15
|
+
// npm global
|
|
16
|
+
try {
|
|
17
|
+
const npmGlobal = execSync("npm root -g", { stdio: "pipe" })
|
|
18
|
+
.toString()
|
|
19
|
+
.trim();
|
|
20
|
+
if (npmGlobal)
|
|
21
|
+
paths.push(npmGlobal);
|
|
22
|
+
}
|
|
23
|
+
catch { }
|
|
24
|
+
return paths;
|
|
25
|
+
}
|
|
26
|
+
export async function importProvider(pkg) {
|
|
27
|
+
// Try normal resolution first (works for local installs)
|
|
28
|
+
try {
|
|
29
|
+
return await import(pkg);
|
|
30
|
+
}
|
|
31
|
+
catch { }
|
|
32
|
+
// Try global node_modules paths
|
|
33
|
+
for (const globalPath of getGlobalNodeModulesPaths()) {
|
|
34
|
+
try {
|
|
35
|
+
const req = createRequire(join(globalPath, ".placeholder"));
|
|
36
|
+
const resolved = req.resolve(pkg);
|
|
37
|
+
return await import(resolved);
|
|
38
|
+
}
|
|
39
|
+
catch { }
|
|
40
|
+
}
|
|
41
|
+
throw new Error(`Provider "${pkg}" is not installed. Run:\n\n npm install -g ${pkg}\n`);
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=providers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"providers.js","sourceRoot":"","sources":["../src/providers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,SAAS,yBAAyB;IACjC,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,cAAc;IACd,IAAI,CAAC;QACJ,MAAM,UAAU,GAAG,QAAQ,CAAC,cAAc,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;aAC5D,QAAQ,EAAE;aACV,IAAI,EAAE,CAAC;QACT,IAAI,UAAU;YAAE,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACxC,CAAC;IAAC,MAAM,CAAC,CAAA,CAAC;IAEV,aAAa;IACb,IAAI,CAAC;QACJ,MAAM,SAAS,GAAG,QAAQ,CAAC,aAAa,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;aAC1D,QAAQ,EAAE;aACV,IAAI,EAAE,CAAC;QACT,IAAI,SAAS;YAAE,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACtC,CAAC;IAAC,MAAM,CAAC,CAAA,CAAC;IAEV,OAAO,KAAK,CAAC;AACd,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CACnC,GAAW;IAEX,yDAAyD;IACzD,IAAI,CAAC;QACJ,OAAO,MAAM,MAAM,CAAC,GAAG,CAAC,CAAC;IAC1B,CAAC;IAAC,MAAM,CAAC,CAAA,CAAC;IAEV,gCAAgC;IAChC,KAAK,MAAM,UAAU,IAAI,yBAAyB,EAAE,EAAE,CAAC;QACtD,IAAI,CAAC;YACJ,MAAM,GAAG,GAAG,aAAa,CAAC,IAAI,CAAC,UAAU,EAAE,cAAc,CAAC,CAAC,CAAC;YAC5D,MAAM,QAAQ,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YAClC,OAAO,MAAM,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC/B,CAAC;QAAC,MAAM,CAAC,CAAA,CAAC;IACX,CAAC;IAED,MAAM,IAAI,KAAK,CACd,aAAa,GAAG,gDAAgD,GAAG,IAAI,CACvE,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { chunkFile } from "./chunker/index.js";
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { deleteByFiles, deleteFileHashes, getChunkCount, getFileHashes, getIndexedFiles, } from "./store.js";
|
|
6
|
+
import { walkFiles } from "./walker.js";
|
|
7
|
+
function log(msg) {
|
|
8
|
+
console.log(`[${new Date().toISOString()}] ${msg}`);
|
|
9
|
+
}
|
|
10
|
+
export async function repairCommand(cwd, opts) {
|
|
11
|
+
log("Scanning index for inconsistencies...");
|
|
12
|
+
const chunkCountBefore = await getChunkCount(cwd);
|
|
13
|
+
const indexedFiles = await getIndexedFiles(cwd);
|
|
14
|
+
const storedFileHashes = await getFileHashes(cwd);
|
|
15
|
+
const diskFiles = new Set(walkFiles(cwd));
|
|
16
|
+
// 1. Find orphaned files: indexed but no longer on disk
|
|
17
|
+
const orphanedFiles = [];
|
|
18
|
+
const allIndexedPaths = new Set([
|
|
19
|
+
...indexedFiles.keys(),
|
|
20
|
+
...storedFileHashes.keys(),
|
|
21
|
+
]);
|
|
22
|
+
for (const fp of allIndexedPaths) {
|
|
23
|
+
if (!diskFiles.has(fp)) {
|
|
24
|
+
orphanedFiles.push(fp);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
// 2. Find stale files: file hash in DB doesn't match current content
|
|
28
|
+
const staleFiles = [];
|
|
29
|
+
for (const fp of diskFiles) {
|
|
30
|
+
const storedHash = storedFileHashes.get(fp);
|
|
31
|
+
if (!storedHash)
|
|
32
|
+
continue; // not tracked, will be picked up by `index`
|
|
33
|
+
try {
|
|
34
|
+
const content = readFileSync(join(cwd, fp));
|
|
35
|
+
const currentHash = createHash("sha256")
|
|
36
|
+
.update(content)
|
|
37
|
+
.digest("hex")
|
|
38
|
+
.slice(0, 16);
|
|
39
|
+
if (storedHash !== currentHash) {
|
|
40
|
+
staleFiles.push(fp);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
// unreadable, treat as orphaned
|
|
45
|
+
orphanedFiles.push(fp);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
// 3. Find files with stale chunks: chunks in DB don't match what chunking produces
|
|
49
|
+
const chunkMismatchFiles = [];
|
|
50
|
+
for (const [fp, indexedHashes] of indexedFiles) {
|
|
51
|
+
if (orphanedFiles.includes(fp) || staleFiles.includes(fp))
|
|
52
|
+
continue;
|
|
53
|
+
if (!diskFiles.has(fp))
|
|
54
|
+
continue;
|
|
55
|
+
try {
|
|
56
|
+
const currentChunks = await chunkFile(fp, cwd);
|
|
57
|
+
const currentHashes = new Set(currentChunks.map((c) => c.hash));
|
|
58
|
+
const indexedSet = new Set(indexedHashes);
|
|
59
|
+
// Check if any indexed chunk is not in current chunks (stale)
|
|
60
|
+
// or if any current chunk is missing from index
|
|
61
|
+
const hasStale = indexedHashes.some((h) => !currentHashes.has(h));
|
|
62
|
+
const hasMissing = currentChunks.some((c) => !indexedSet.has(c.hash));
|
|
63
|
+
if (hasStale || hasMissing) {
|
|
64
|
+
chunkMismatchFiles.push(fp);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
// skip files that fail to parse
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
const totalProblems = orphanedFiles.length + staleFiles.length + chunkMismatchFiles.length;
|
|
72
|
+
if (totalProblems === 0) {
|
|
73
|
+
log("Index is consistent. No repairs needed.");
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
log(`Found ${totalProblems} issues:`);
|
|
77
|
+
if (orphanedFiles.length > 0) {
|
|
78
|
+
log(` ${orphanedFiles.length} orphaned files (deleted from disk)`);
|
|
79
|
+
}
|
|
80
|
+
if (staleFiles.length > 0) {
|
|
81
|
+
log(` ${staleFiles.length} stale files (content changed, hash outdated)`);
|
|
82
|
+
}
|
|
83
|
+
if (chunkMismatchFiles.length > 0) {
|
|
84
|
+
log(` ${chunkMismatchFiles.length} files with mismatched chunks`);
|
|
85
|
+
}
|
|
86
|
+
if (opts.dry) {
|
|
87
|
+
for (const fp of orphanedFiles)
|
|
88
|
+
log(` [orphan] ${fp}`);
|
|
89
|
+
for (const fp of staleFiles)
|
|
90
|
+
log(` [stale] ${fp}`);
|
|
91
|
+
for (const fp of chunkMismatchFiles)
|
|
92
|
+
log(` [mismatch] ${fp}`);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
// Remove orphaned files from both tables
|
|
96
|
+
if (orphanedFiles.length > 0) {
|
|
97
|
+
await deleteByFiles(orphanedFiles, cwd);
|
|
98
|
+
await deleteFileHashes(orphanedFiles, cwd);
|
|
99
|
+
log(`Removed ${orphanedFiles.length} orphaned files from index`);
|
|
100
|
+
}
|
|
101
|
+
// Remove stale file hashes so `index` will re-process them
|
|
102
|
+
const filesToInvalidate = [...staleFiles, ...chunkMismatchFiles];
|
|
103
|
+
if (filesToInvalidate.length > 0) {
|
|
104
|
+
await deleteByFiles(filesToInvalidate, cwd);
|
|
105
|
+
await deleteFileHashes(filesToInvalidate, cwd);
|
|
106
|
+
log(`Invalidated ${filesToInvalidate.length} files — run \`lmgrep index\` to re-embed`);
|
|
107
|
+
}
|
|
108
|
+
const chunkCountAfter = await getChunkCount(cwd);
|
|
109
|
+
const removed = chunkCountBefore - chunkCountAfter;
|
|
110
|
+
log(`Repair complete. Removed ${removed} stale chunks.`);
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=repair-cmd.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repair-cmd.js","sourceRoot":"","sources":["../src/repair-cmd.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
interface SearchOptions {
|
|
2
|
+
limit?: number;
|
|
3
|
+
scores?: boolean;
|
|
4
|
+
compact?: boolean;
|
|
5
|
+
minScore?: number;
|
|
6
|
+
filePrefix?: string;
|
|
7
|
+
not?: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function searchCommand(query: string, cwd: string, opts: SearchOptions): Promise<void>;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { loadConfig } from "./config.js";
|
|
2
|
+
import { embedQuery } from "./embedder.js";
|
|
3
|
+
import { findIndexedAncestor, search } from "./store.js";
|
|
4
|
+
export async function searchCommand(query, cwd, opts) {
|
|
5
|
+
// Auto-detect indexed parent and use subdirectory as prefix
|
|
6
|
+
let searchCwd = cwd;
|
|
7
|
+
let filePrefix = opts.filePrefix;
|
|
8
|
+
const ancestor = findIndexedAncestor(cwd);
|
|
9
|
+
if (ancestor && ancestor.prefix) {
|
|
10
|
+
searchCwd = ancestor.root;
|
|
11
|
+
// Combine auto-detected prefix with explicit --file-prefix
|
|
12
|
+
filePrefix = filePrefix
|
|
13
|
+
? `${ancestor.prefix}/${filePrefix}`
|
|
14
|
+
: ancestor.prefix;
|
|
15
|
+
}
|
|
16
|
+
const config = loadConfig(searchCwd);
|
|
17
|
+
const limit = opts.limit ?? 25;
|
|
18
|
+
let queryVector = await embedQuery(query, config);
|
|
19
|
+
// Subtract the --not vector to push away from unwanted results
|
|
20
|
+
if (opts.not) {
|
|
21
|
+
const notVector = await embedQuery(opts.not, config);
|
|
22
|
+
queryVector = queryVector.map((v, i) => v - notVector[i] * 0.5);
|
|
23
|
+
}
|
|
24
|
+
let results = await search(queryVector, searchCwd, limit, filePrefix);
|
|
25
|
+
if (opts.minScore != null) {
|
|
26
|
+
results = results.filter((r) => r.score >= opts.minScore);
|
|
27
|
+
}
|
|
28
|
+
if (results.length === 0) {
|
|
29
|
+
console.log("No results found.");
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
if (opts.compact) {
|
|
33
|
+
printCompact(results);
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
printFull(results, opts.scores);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function printCompact(results) {
|
|
40
|
+
const seen = new Set();
|
|
41
|
+
for (const r of results) {
|
|
42
|
+
if (!seen.has(r.filePath)) {
|
|
43
|
+
seen.add(r.filePath);
|
|
44
|
+
console.log(r.filePath);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function printFull(results, showScores = false) {
|
|
49
|
+
for (const r of results) {
|
|
50
|
+
const header = `${r.filePath}:${r.startLine}-${r.endLine} [${r.type}] ${r.name}`;
|
|
51
|
+
const score = showScores ? ` (score: ${r.score.toFixed(3)})` : "";
|
|
52
|
+
console.log(`\n${"─".repeat(60)}`);
|
|
53
|
+
console.log(`${header}${score}`);
|
|
54
|
+
console.log(`${"─".repeat(60)}`);
|
|
55
|
+
console.log(r.context);
|
|
56
|
+
console.log();
|
|
57
|
+
console.log(r.content);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=search-cmd.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search-cmd.js","sourceRoot":"","sources":["../src/search-cmd.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function serveCommand(cwd: string): Promise<void>;
|