memonaut 0.0.0 → 0.2.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 +661 -0
- package/dist/cli-main.d.ts +14 -0
- package/dist/cli-main.d.ts.map +1 -0
- package/dist/cli-main.js +551 -0
- package/dist/cli-main.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +16 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +38 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +86 -0
- package/dist/config.js.map +1 -0
- package/dist/db.d.ts +37 -0
- package/dist/db.d.ts.map +1 -0
- package/dist/db.js +184 -0
- package/dist/db.js.map +1 -0
- package/dist/format.d.ts +22 -0
- package/dist/format.d.ts.map +1 -0
- package/dist/format.js +143 -0
- package/dist/format.js.map +1 -0
- package/dist/glob.d.ts +17 -0
- package/dist/glob.d.ts.map +1 -0
- package/dist/glob.js +77 -0
- package/dist/glob.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/indexer.d.ts +50 -0
- package/dist/indexer.d.ts.map +1 -0
- package/dist/indexer.js +404 -0
- package/dist/indexer.js.map +1 -0
- package/dist/lineage.d.ts +31 -0
- package/dist/lineage.d.ts.map +1 -0
- package/dist/lineage.js +94 -0
- package/dist/lineage.js.map +1 -0
- package/dist/model.d.ts +121 -0
- package/dist/model.d.ts.map +1 -0
- package/dist/model.js +41 -0
- package/dist/model.js.map +1 -0
- package/dist/pi-source.d.ts +47 -0
- package/dist/pi-source.d.ts.map +1 -0
- package/dist/pi-source.js +309 -0
- package/dist/pi-source.js.map +1 -0
- package/dist/quiet.d.ts +7 -0
- package/dist/quiet.d.ts.map +1 -0
- package/dist/quiet.js +19 -0
- package/dist/quiet.js.map +1 -0
- package/dist/regex.d.ts +75 -0
- package/dist/regex.d.ts.map +1 -0
- package/dist/regex.js +242 -0
- package/dist/regex.js.map +1 -0
- package/dist/ripgrep.d.ts +52 -0
- package/dist/ripgrep.d.ts.map +1 -0
- package/dist/ripgrep.js +217 -0
- package/dist/ripgrep.js.map +1 -0
- package/dist/search.d.ts +114 -0
- package/dist/search.d.ts.map +1 -0
- package/dist/search.js +309 -0
- package/dist/search.js.map +1 -0
- package/dist/silence-sqlite-warning.d.ts +2 -0
- package/dist/silence-sqlite-warning.d.ts.map +1 -0
- package/dist/silence-sqlite-warning.js +9 -0
- package/dist/silence-sqlite-warning.js.map +1 -0
- package/package.json +57 -2
- package/src/cli-main.ts +618 -0
- package/src/cli.ts +17 -0
- package/src/config.ts +130 -0
- package/src/db.ts +213 -0
- package/src/format.ts +185 -0
- package/src/glob.ts +79 -0
- package/src/index.ts +19 -0
- package/src/indexer.ts +534 -0
- package/src/lineage.ts +111 -0
- package/src/model.ts +157 -0
- package/src/pi-source.ts +328 -0
- package/src/quiet.ts +22 -0
- package/src/regex.ts +378 -0
- package/src/ripgrep.ts +263 -0
- package/src/search.ts +504 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"regex.d.ts","sourceRoot":"","sources":["../src/regex.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,EAAE,EAAC,MAAM,SAAS,CAAC;AAChC,OAAO,KAAK,EAAC,SAAS,EAAE,SAAS,EAAC,MAAM,YAAY,CAAC;AAKrD;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,MAAM,WAAW,UAAU;IAC1B,wEAAwE;IACxE,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,IAAI,CAAC,EAAE,SAAS,EAAE,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,8DAA8D;IAC9D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe;IACf,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,gDAAgD;IAChD,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,4CAA4C;IAC5C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,+DAA+D;IAC/D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,6DAA6D;IAC7D,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,YAAY;IAC5B,IAAI,EAAE,SAAS,EAAE,CAAC;IAClB,qEAAqE;IACrE,aAAa,EAAE,MAAM,CAAC;IACtB,6DAA6D;IAC7D,UAAU,EAAE,MAAM,CAAC;IACnB,sDAAsD;IACtD,cAAc,EAAE,MAAM,CAAC;IACvB,+CAA+C;IAC/C,OAAO,EAAE,MAAM,CAAC;IAChB,yEAAyE;IACzE,SAAS,EAAE,OAAO,CAAC;IACnB;;;OAGG;IACH,UAAU,EAAE,MAAM,CAAC;CACnB;AAED,eAAO,MAAM,oBAAoB,MAAM,CAAC;AACxC,eAAO,MAAM,mBAAmB,OAAO,CAAC;AA8GxC;;;GAGG;AACH,wBAAsB,WAAW,CAChC,EAAE,EAAE,EAAE,EACN,KAAK,EAAE,UAAU,GACf,OAAO,CAAC,YAAY,CAAC,CAyLvB"}
|
package/dist/regex.js
ADDED
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import { extractEntry } from './pi-source.js';
|
|
2
|
+
import { ripgrep } from './ripgrep.js';
|
|
3
|
+
import { collectThreads, loadFiles, visibleSets } from './search.js';
|
|
4
|
+
export const DEFAULT_MAX_PER_FILE = 200;
|
|
5
|
+
export const DEFAULT_MAX_MATCHES = 2000;
|
|
6
|
+
/** Context kept either side of the match in a snippet, in characters. */
|
|
7
|
+
const SNIPPET_RADIUS = 70;
|
|
8
|
+
/** Everything the extractor can see, so the snippet is not limited by the tier. */
|
|
9
|
+
const FULL_EXTRACT = {
|
|
10
|
+
tier: 'full',
|
|
11
|
+
toolResultHeadBytes: Number.MAX_SAFE_INTEGER,
|
|
12
|
+
toolArgsHeadBytes: Number.MAX_SAFE_INTEGER,
|
|
13
|
+
};
|
|
14
|
+
function windowAround(text, start, length, radius = SNIPPET_RADIUS) {
|
|
15
|
+
const from = Math.max(0, start - radius);
|
|
16
|
+
const to = Math.min(text.length, start + length + radius);
|
|
17
|
+
// \u0001/\u0002 are the same markers FTS5 `snippet()` emits, so the renderer
|
|
18
|
+
// paints a regex hit exactly like a normal one.
|
|
19
|
+
return ((from > 0 ? '…' : '') +
|
|
20
|
+
text.slice(from, start) +
|
|
21
|
+
'\u0001' +
|
|
22
|
+
text.slice(start, start + length) +
|
|
23
|
+
'\u0002' +
|
|
24
|
+
text.slice(start + length, to) +
|
|
25
|
+
(to < text.length ? '…' : ''));
|
|
26
|
+
}
|
|
27
|
+
function indexOfNeedle(haystack, needle, ignoreCase) {
|
|
28
|
+
if (!needle)
|
|
29
|
+
return -1;
|
|
30
|
+
if (!ignoreCase)
|
|
31
|
+
return haystack.indexOf(needle);
|
|
32
|
+
return haystack.toLowerCase().indexOf(needle.toLowerCase());
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Turn a matched raw line into something a human can read, and say where the
|
|
36
|
+
* matched bytes actually live.
|
|
37
|
+
*
|
|
38
|
+
* The needle is rg's own `submatches[0]`, which is a LITERAL whatever the
|
|
39
|
+
* pattern was, so nothing here has to re-implement Rust regex semantics in
|
|
40
|
+
* JavaScript.
|
|
41
|
+
*
|
|
42
|
+
* Three outcomes, and keeping them apart is the honesty of the whole feature:
|
|
43
|
+
* the needle is in extracted text that is also indexed (`index`); it is in
|
|
44
|
+
* extracted text that is NOT indexed, because of the tier or because the chunk
|
|
45
|
+
* was truncated before it (`transcript`); or it is in none of the extracted
|
|
46
|
+
* text at all, so it matched a JSON key, an id or an escape sequence
|
|
47
|
+
* (`structure`). Only the middle one means "the index could never have found
|
|
48
|
+
* this". Labelling a match on `"timestamp":` that way would be a lie about an
|
|
49
|
+
* entry whose text is indexed perfectly well.
|
|
50
|
+
*/
|
|
51
|
+
function describeMatch(raw, match, ignoreCase, indexedTexts) {
|
|
52
|
+
const extracted = extractEntry(raw, 0, 0, FULL_EXTRACT)?.chunks ?? [];
|
|
53
|
+
for (const chunk of extracted) {
|
|
54
|
+
const at = indexOfNeedle(chunk.text, match.needle, ignoreCase);
|
|
55
|
+
if (at < 0)
|
|
56
|
+
continue;
|
|
57
|
+
const indexed = indexedTexts.some((text) => indexOfNeedle(text, match.needle, ignoreCase) >= 0);
|
|
58
|
+
return {
|
|
59
|
+
kind: chunk.kind,
|
|
60
|
+
snippet: windowAround(chunk.text, at, match.needle.length),
|
|
61
|
+
matchedIn: indexed ? 'index' : 'transcript',
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
// Nothing anyone wrote: no kind fits, so none is invented.
|
|
65
|
+
const at = indexOfNeedle(match.line, match.needle, ignoreCase);
|
|
66
|
+
return {
|
|
67
|
+
snippet: at >= 0
|
|
68
|
+
? windowAround(match.line.trimEnd(), at, match.needle.length)
|
|
69
|
+
: match.line.slice(0, SNIPPET_RADIUS * 2),
|
|
70
|
+
matchedIn: 'structure',
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Search the transcripts themselves, and report the result exactly as the
|
|
75
|
+
* index-backed search would.
|
|
76
|
+
*/
|
|
77
|
+
export async function regexSearch(db, query) {
|
|
78
|
+
const limit = query.limit ?? 20;
|
|
79
|
+
const threadLimit = query.threadLimit ?? 3;
|
|
80
|
+
// A cap that is NaN or zero would mean "collect every match in the corpus",
|
|
81
|
+
// which is exactly the unbounded read this is here to prevent.
|
|
82
|
+
const maxMatches = query.maxMatches !== undefined &&
|
|
83
|
+
Number.isFinite(query.maxMatches) &&
|
|
84
|
+
query.maxMatches > 0
|
|
85
|
+
? query.maxMatches
|
|
86
|
+
: DEFAULT_MAX_MATCHES;
|
|
87
|
+
const files = loadFiles(db);
|
|
88
|
+
const visible = visibleSets(files, query);
|
|
89
|
+
// The earliest moment a lineage could hold: a fork's own `started` is later
|
|
90
|
+
// than the entries it inherited, so an `until` bound must be judged against
|
|
91
|
+
// the whole family or a fork whose parent is gone from disk would be skipped.
|
|
92
|
+
const lineageStart = new Map();
|
|
93
|
+
for (const file of files.values()) {
|
|
94
|
+
if (file.lineage_id === null || !file.started)
|
|
95
|
+
continue;
|
|
96
|
+
const id = Number(file.lineage_id);
|
|
97
|
+
const known = lineageStart.get(id);
|
|
98
|
+
if (!known || file.started < known)
|
|
99
|
+
lineageStart.set(id, file.started);
|
|
100
|
+
}
|
|
101
|
+
// Path to lineage: the lineage is all a matched line needs, because entry
|
|
102
|
+
// identity is (lineage_id, entry_key) and a fork copy resolves to the entry
|
|
103
|
+
// its ancestor owns.
|
|
104
|
+
const byPath = new Map();
|
|
105
|
+
const paths = [];
|
|
106
|
+
for (const file of files.values()) {
|
|
107
|
+
const fileId = Number(file.id);
|
|
108
|
+
if (!visible.files.has(fileId))
|
|
109
|
+
continue;
|
|
110
|
+
if (file.lineage_id === null)
|
|
111
|
+
continue;
|
|
112
|
+
const lineageId = Number(file.lineage_id);
|
|
113
|
+
// Time prefilters, kept conservative: `last_activity` is the newest entry
|
|
114
|
+
// in this file INCLUDING inherited ones, so it is a sound `since` bound.
|
|
115
|
+
if (query.since && file.last_activity && file.last_activity < query.since)
|
|
116
|
+
continue;
|
|
117
|
+
const start = lineageStart.get(lineageId);
|
|
118
|
+
if (query.until && start && start > query.until)
|
|
119
|
+
continue;
|
|
120
|
+
byPath.set(file.path, lineageId);
|
|
121
|
+
paths.push(file.path);
|
|
122
|
+
}
|
|
123
|
+
const outcome = {
|
|
124
|
+
hits: [],
|
|
125
|
+
filesSearched: paths.length,
|
|
126
|
+
rawMatches: 0,
|
|
127
|
+
entriesMatched: 0,
|
|
128
|
+
scanned: 0,
|
|
129
|
+
truncated: false,
|
|
130
|
+
unresolved: 0,
|
|
131
|
+
};
|
|
132
|
+
if (paths.length === 0)
|
|
133
|
+
return outcome;
|
|
134
|
+
const entryStmt = db.prepare(`SELECT id, entry_key, lineage_id, role, tool, ts
|
|
135
|
+
FROM entry WHERE lineage_id = ? AND entry_key = ?`);
|
|
136
|
+
const chunkStmt = db.prepare('SELECT text FROM chunk WHERE entry_id = ?');
|
|
137
|
+
const roleFilter = query.role?.length ? new Set(query.role) : null;
|
|
138
|
+
const toolFilter = query.tool?.length ? new Set(query.tool) : null;
|
|
139
|
+
const kindFilter = query.kind?.length ? new Set(query.kind) : null;
|
|
140
|
+
const seen = new Set();
|
|
141
|
+
const candidates = [];
|
|
142
|
+
await ripgrep(paths, {
|
|
143
|
+
pattern: query.pattern,
|
|
144
|
+
ignoreCase: query.ignoreCase,
|
|
145
|
+
fixed: query.fixed,
|
|
146
|
+
maxPerFile: query.maxPerFile ?? DEFAULT_MAX_PER_FILE,
|
|
147
|
+
binary: query.binary,
|
|
148
|
+
}, (match) => {
|
|
149
|
+
outcome.rawMatches++;
|
|
150
|
+
const lineage = byPath.get(match.path);
|
|
151
|
+
if (lineage === undefined)
|
|
152
|
+
return;
|
|
153
|
+
let raw;
|
|
154
|
+
try {
|
|
155
|
+
raw = JSON.parse(match.line);
|
|
156
|
+
}
|
|
157
|
+
catch {
|
|
158
|
+
outcome.unresolved++;
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
const entryKey = typeof raw.id === 'string' ? raw.id : null;
|
|
162
|
+
if (!entryKey)
|
|
163
|
+
return; // the session header line carries no identity
|
|
164
|
+
const entry = entryStmt.get(lineage, entryKey);
|
|
165
|
+
if (!entry) {
|
|
166
|
+
outcome.unresolved++;
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
// THE fork rule: a shared entry is reported by rg once per fork file
|
|
170
|
+
// that copied it, and must collapse to one hit here. `(lineage, key)`
|
|
171
|
+
// resolved to one entry row, so its id is the deduplication key.
|
|
172
|
+
const entryId = Number(entry.id);
|
|
173
|
+
if (seen.has(entryId))
|
|
174
|
+
return;
|
|
175
|
+
seen.add(entryId);
|
|
176
|
+
if (roleFilter && !roleFilter.has(entry.role))
|
|
177
|
+
return;
|
|
178
|
+
if (toolFilter && !(entry.tool && toolFilter.has(entry.tool)))
|
|
179
|
+
return;
|
|
180
|
+
if (query.since && (!entry.ts || entry.ts < query.since))
|
|
181
|
+
return;
|
|
182
|
+
if (query.until && (!entry.ts || entry.ts > query.until))
|
|
183
|
+
return;
|
|
184
|
+
const indexedTexts = chunkStmt.all(entryId).map((r) => r.text);
|
|
185
|
+
const described = describeMatch(raw, match, Boolean(query.ignoreCase), indexedTexts);
|
|
186
|
+
// A structural match has no kind, so a --kind filter excludes it: it is
|
|
187
|
+
// not text of that kind, and pretending otherwise would be a guess.
|
|
188
|
+
if (kindFilter && !(described.kind && kindFilter.has(described.kind)))
|
|
189
|
+
return;
|
|
190
|
+
candidates.push({ entry, ...described });
|
|
191
|
+
if (candidates.length >= maxMatches) {
|
|
192
|
+
outcome.truncated = true;
|
|
193
|
+
return false;
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
outcome.entriesMatched = candidates.length;
|
|
197
|
+
// No bm25 here, and inventing a score would be a lie. Recency of the matched
|
|
198
|
+
// entry is the ordering, newest first.
|
|
199
|
+
candidates.sort((a, b) => (b.entry.ts ?? '').localeCompare(a.entry.ts ?? ''));
|
|
200
|
+
const memberStmt = db.prepare('SELECT file_id, seq FROM membership WHERE entry_id = ?');
|
|
201
|
+
const groups = new Map();
|
|
202
|
+
const order = [];
|
|
203
|
+
for (const candidate of candidates) {
|
|
204
|
+
const lineageId = Number(candidate.entry.lineage_id);
|
|
205
|
+
const existing = groups.get(lineageId);
|
|
206
|
+
if (existing) {
|
|
207
|
+
// Same grouping as the FTS path: one chatty lineage cannot own the page,
|
|
208
|
+
// and the matches it displaced are counted rather than dropped silently.
|
|
209
|
+
existing.otherHits++;
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
212
|
+
const threads = collectThreads(Number(candidate.entry.id), memberStmt, files, visible.files);
|
|
213
|
+
if (threads.length === 0)
|
|
214
|
+
continue;
|
|
215
|
+
const hit = {
|
|
216
|
+
entryId: Number(candidate.entry.id),
|
|
217
|
+
entryKey: candidate.entry.entry_key,
|
|
218
|
+
lineageId,
|
|
219
|
+
role: candidate.entry.role,
|
|
220
|
+
tool: candidate.entry.tool,
|
|
221
|
+
ts: candidate.entry.ts,
|
|
222
|
+
kind: candidate.kind,
|
|
223
|
+
snippet: candidate.snippet,
|
|
224
|
+
// No bm25 to rank with, so no score is claimed; the array order is the
|
|
225
|
+
// answer, newest match first.
|
|
226
|
+
score: 0,
|
|
227
|
+
threads: threadLimit < 0 ? threads : threads.slice(0, Math.max(threadLimit, 1)),
|
|
228
|
+
threadTotal: threads.length,
|
|
229
|
+
otherHits: 0,
|
|
230
|
+
matchedIn: candidate.matchedIn,
|
|
231
|
+
};
|
|
232
|
+
groups.set(lineageId, hit);
|
|
233
|
+
order.push(lineageId);
|
|
234
|
+
}
|
|
235
|
+
outcome.scanned = order.length;
|
|
236
|
+
outcome.hits = order
|
|
237
|
+
.map((id) => groups.get(id))
|
|
238
|
+
.filter((h) => Boolean(h))
|
|
239
|
+
.slice(0, limit);
|
|
240
|
+
return outcome;
|
|
241
|
+
}
|
|
242
|
+
//# sourceMappingURL=regex.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"regex.js","sourceRoot":"","sources":["../src/regex.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thin wrapper around `rg --json`.
|
|
3
|
+
*
|
|
4
|
+
* Regex over the raw transcripts is a genuinely different capability from the
|
|
5
|
+
* FTS index (see `docs/DESIGN.md`): FTS5 tokenises, so it can never do
|
|
6
|
+
* substring-inside-identifier or true regex, and the raw files hold text the
|
|
7
|
+
* index deliberately does not (tool output is 42% of the bytes). Rather than
|
|
8
|
+
* reimplement a scanner over 2 GB of JSONL in JavaScript, this shells out to
|
|
9
|
+
* the tool that already does it well, and refuses to pretend when it is absent.
|
|
10
|
+
*/
|
|
11
|
+
export declare const DEFAULT_RG = "rg";
|
|
12
|
+
export declare function ripgrepBinary(env?: NodeJS.ProcessEnv): string;
|
|
13
|
+
export declare class RipgrepMissingError extends Error {
|
|
14
|
+
readonly binary: string;
|
|
15
|
+
constructor(binary: string);
|
|
16
|
+
}
|
|
17
|
+
export declare class RipgrepError extends Error {
|
|
18
|
+
constructor(message: string);
|
|
19
|
+
}
|
|
20
|
+
/** One matching line, as rg reported it. */
|
|
21
|
+
export interface RipgrepMatch {
|
|
22
|
+
/** Path exactly as it was handed to rg, so it can be looked up in the index. */
|
|
23
|
+
path: string;
|
|
24
|
+
/** The whole matching line, which for a transcript is one complete JSON entry. */
|
|
25
|
+
line: string;
|
|
26
|
+
/** Byte offset of the line within the file. */
|
|
27
|
+
offset: number;
|
|
28
|
+
/** The literal text the pattern matched. A literal, whatever the pattern was. */
|
|
29
|
+
needle: string;
|
|
30
|
+
}
|
|
31
|
+
export interface RipgrepOptions {
|
|
32
|
+
pattern: string;
|
|
33
|
+
ignoreCase?: boolean;
|
|
34
|
+
/** `-F`: the pattern is a literal string, not a regex. */
|
|
35
|
+
fixed?: boolean;
|
|
36
|
+
/** `-m`: per-file cap. 0 disables it. */
|
|
37
|
+
maxPerFile?: number;
|
|
38
|
+
binary?: string;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Run rg over `paths`, calling `onMatch` for each matching line.
|
|
42
|
+
*
|
|
43
|
+
* Output is consumed as it arrives, so at most ONE record is ever held: a
|
|
44
|
+
* single transcript line can be megabytes (a base64 image), and `--json` echoes
|
|
45
|
+
* the whole line back, so accumulating rg's output is not an option. What
|
|
46
|
+
* `onMatch` then does with that one line is its own business, and runs inline
|
|
47
|
+
* on this handler. Returning `false` stops the scan and kills rg.
|
|
48
|
+
*/
|
|
49
|
+
export declare function ripgrep(paths: string[], opts: RipgrepOptions, onMatch: (match: RipgrepMatch) => boolean | void): Promise<void>;
|
|
50
|
+
/** True when a usable ripgrep is on PATH. For tests and for `--help` honesty. */
|
|
51
|
+
export declare function ripgrepAvailable(binary?: string): Promise<boolean>;
|
|
52
|
+
//# sourceMappingURL=ripgrep.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ripgrep.d.ts","sourceRoot":"","sources":["../src/ripgrep.ts"],"names":[],"mappings":"AAEA;;;;;;;;;GASG;AAEH,eAAO,MAAM,UAAU,OAAO,CAAC;AAE/B,wBAAgB,aAAa,CAAC,GAAG,oBAAc,GAAG,MAAM,CAEvD;AAED,qBAAa,mBAAoB,SAAQ,KAAK;IAC7C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;gBACZ,MAAM,EAAE,MAAM;CAW1B;AAED,qBAAa,YAAa,SAAQ,KAAK;gBAC1B,OAAO,EAAE,MAAM;CAI3B;AAED,4CAA4C;AAC5C,MAAM,WAAW,YAAY;IAC5B,gFAAgF;IAChF,IAAI,EAAE,MAAM,CAAC;IACb,kFAAkF;IAClF,IAAI,EAAE,MAAM,CAAC;IACb,+CAA+C;IAC/C,MAAM,EAAE,MAAM,CAAC;IACf,iFAAiF;IACjF,MAAM,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,cAAc;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,0DAA0D;IAC1D,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,yCAAyC;IACzC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAiED;;;;;;;;GAQG;AACH,wBAAsB,OAAO,CAC5B,KAAK,EAAE,MAAM,EAAE,EACf,IAAI,EAAE,cAAc,EACpB,OAAO,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,OAAO,GAAG,IAAI,GAC9C,OAAO,CAAC,IAAI,CAAC,CAgBf;AA2FD,iFAAiF;AACjF,wBAAsB,gBAAgB,CACrC,MAAM,SAAkB,GACtB,OAAO,CAAC,OAAO,CAAC,CAalB"}
|
package/dist/ripgrep.js
ADDED
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
/**
|
|
3
|
+
* Thin wrapper around `rg --json`.
|
|
4
|
+
*
|
|
5
|
+
* Regex over the raw transcripts is a genuinely different capability from the
|
|
6
|
+
* FTS index (see `docs/DESIGN.md`): FTS5 tokenises, so it can never do
|
|
7
|
+
* substring-inside-identifier or true regex, and the raw files hold text the
|
|
8
|
+
* index deliberately does not (tool output is 42% of the bytes). Rather than
|
|
9
|
+
* reimplement a scanner over 2 GB of JSONL in JavaScript, this shells out to
|
|
10
|
+
* the tool that already does it well, and refuses to pretend when it is absent.
|
|
11
|
+
*/
|
|
12
|
+
export const DEFAULT_RG = 'rg';
|
|
13
|
+
export function ripgrepBinary(env = process.env) {
|
|
14
|
+
return env.MEMONAUT_RG?.trim() || DEFAULT_RG;
|
|
15
|
+
}
|
|
16
|
+
export class RipgrepMissingError extends Error {
|
|
17
|
+
binary;
|
|
18
|
+
constructor(binary) {
|
|
19
|
+
super([
|
|
20
|
+
`regex search needs ripgrep, and \`${binary}\` was not found.`,
|
|
21
|
+
' install it: apt install ripgrep · brew install ripgrep · cargo install ripgrep',
|
|
22
|
+
' or point MEMONAUT_RG at the binary.',
|
|
23
|
+
].join('\n'));
|
|
24
|
+
this.name = 'RipgrepMissingError';
|
|
25
|
+
this.binary = binary;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export class RipgrepError extends Error {
|
|
29
|
+
constructor(message) {
|
|
30
|
+
super(`ripgrep: ${message}`);
|
|
31
|
+
this.name = 'RipgrepError';
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* argv budget per invocation.
|
|
36
|
+
*
|
|
37
|
+
* `ARG_MAX` is ~2 MB on Linux and 256 KB on macOS, and a transcript path is
|
|
38
|
+
* ~110 bytes, so batching by both count and byte length keeps every platform
|
|
39
|
+
* far away from E2BIG.
|
|
40
|
+
*/
|
|
41
|
+
const BATCH_FILES = 400;
|
|
42
|
+
const BATCH_BYTES = 96_000;
|
|
43
|
+
function decode(field) {
|
|
44
|
+
if (!field)
|
|
45
|
+
return '';
|
|
46
|
+
if (typeof field.text === 'string')
|
|
47
|
+
return field.text;
|
|
48
|
+
if (typeof field.bytes === 'string')
|
|
49
|
+
return Buffer.from(field.bytes, 'base64').toString('utf8');
|
|
50
|
+
return '';
|
|
51
|
+
}
|
|
52
|
+
function batches(paths) {
|
|
53
|
+
const out = [];
|
|
54
|
+
let current = [];
|
|
55
|
+
let bytes = 0;
|
|
56
|
+
for (const p of paths) {
|
|
57
|
+
if (current.length > 0 &&
|
|
58
|
+
(current.length >= BATCH_FILES || bytes + p.length + 1 > BATCH_BYTES)) {
|
|
59
|
+
out.push(current);
|
|
60
|
+
current = [];
|
|
61
|
+
bytes = 0;
|
|
62
|
+
}
|
|
63
|
+
current.push(p);
|
|
64
|
+
bytes += p.length + 1;
|
|
65
|
+
}
|
|
66
|
+
if (current.length)
|
|
67
|
+
out.push(current);
|
|
68
|
+
return out;
|
|
69
|
+
}
|
|
70
|
+
function baseArgs(opts) {
|
|
71
|
+
const args = [
|
|
72
|
+
'--json',
|
|
73
|
+
// Errors about files that vanished between the index scan and now are not
|
|
74
|
+
// the user's problem; a real pattern error still reaches stderr.
|
|
75
|
+
'--no-messages',
|
|
76
|
+
// The user's ripgrep config must not change what a search means here.
|
|
77
|
+
'--no-config',
|
|
78
|
+
// Explicit paths already come from the index, which is the only ignore
|
|
79
|
+
// list that matters. --text stops a transcript being silently skipped as
|
|
80
|
+
// binary; they are JSON, so there is nothing to blow up on.
|
|
81
|
+
'--no-ignore',
|
|
82
|
+
'--text',
|
|
83
|
+
];
|
|
84
|
+
if (opts.ignoreCase)
|
|
85
|
+
args.push('-i');
|
|
86
|
+
if (opts.fixed)
|
|
87
|
+
args.push('-F');
|
|
88
|
+
// Explicitly finite: a NaN cap must not take the same branch as the
|
|
89
|
+
// documented "0 means all", or a mistyped flag silently removes the bound.
|
|
90
|
+
if (Number.isFinite(opts.maxPerFile) && opts.maxPerFile > 0)
|
|
91
|
+
args.push('-m', String(opts.maxPerFile));
|
|
92
|
+
// -e, so a pattern starting with `-` is a pattern and not a flag.
|
|
93
|
+
args.push('-e', opts.pattern);
|
|
94
|
+
return args;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Run rg over `paths`, calling `onMatch` for each matching line.
|
|
98
|
+
*
|
|
99
|
+
* Output is consumed as it arrives, so at most ONE record is ever held: a
|
|
100
|
+
* single transcript line can be megabytes (a base64 image), and `--json` echoes
|
|
101
|
+
* the whole line back, so accumulating rg's output is not an option. What
|
|
102
|
+
* `onMatch` then does with that one line is its own business, and runs inline
|
|
103
|
+
* on this handler. Returning `false` stops the scan and kills rg.
|
|
104
|
+
*/
|
|
105
|
+
export async function ripgrep(paths, opts, onMatch) {
|
|
106
|
+
if (paths.length === 0)
|
|
107
|
+
return;
|
|
108
|
+
const binary = opts.binary ?? ripgrepBinary();
|
|
109
|
+
const args = baseArgs(opts);
|
|
110
|
+
let stop = false;
|
|
111
|
+
for (const batch of batches(paths)) {
|
|
112
|
+
if (stop)
|
|
113
|
+
return;
|
|
114
|
+
await runOne(binary, [...args, '--', ...batch], (match) => {
|
|
115
|
+
const keepGoing = onMatch(match);
|
|
116
|
+
if (keepGoing === false) {
|
|
117
|
+
stop = true;
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
return true;
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
function runOne(binary, args, onMatch) {
|
|
125
|
+
return new Promise((resolve, reject) => {
|
|
126
|
+
const child = spawn(binary, args, { stdio: ['ignore', 'pipe', 'pipe'] });
|
|
127
|
+
let stderr = '';
|
|
128
|
+
let buffer = '';
|
|
129
|
+
let killed = false;
|
|
130
|
+
let settled = false;
|
|
131
|
+
const finish = (err) => {
|
|
132
|
+
if (settled)
|
|
133
|
+
return;
|
|
134
|
+
settled = true;
|
|
135
|
+
if (err)
|
|
136
|
+
reject(err);
|
|
137
|
+
else
|
|
138
|
+
resolve();
|
|
139
|
+
};
|
|
140
|
+
child.on('error', (err) => {
|
|
141
|
+
const code = err.code;
|
|
142
|
+
if (code === 'ENOENT')
|
|
143
|
+
finish(new RipgrepMissingError(binary));
|
|
144
|
+
else
|
|
145
|
+
finish(err);
|
|
146
|
+
});
|
|
147
|
+
child.stderr.setEncoding('utf8');
|
|
148
|
+
child.stderr.on('data', (chunk) => {
|
|
149
|
+
if (stderr.length < 4096)
|
|
150
|
+
stderr += chunk;
|
|
151
|
+
});
|
|
152
|
+
child.stdout.setEncoding('utf8');
|
|
153
|
+
child.stdout.on('error', () => {
|
|
154
|
+
/* EPIPE after an early kill */
|
|
155
|
+
});
|
|
156
|
+
child.stdout.on('data', (chunk) => {
|
|
157
|
+
if (killed)
|
|
158
|
+
return;
|
|
159
|
+
buffer += chunk;
|
|
160
|
+
let nl;
|
|
161
|
+
while ((nl = buffer.indexOf('\n')) >= 0) {
|
|
162
|
+
const line = buffer.slice(0, nl);
|
|
163
|
+
buffer = buffer.slice(nl + 1);
|
|
164
|
+
if (!line)
|
|
165
|
+
continue;
|
|
166
|
+
let record;
|
|
167
|
+
try {
|
|
168
|
+
record = JSON.parse(line);
|
|
169
|
+
}
|
|
170
|
+
catch {
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
if (record.type !== 'match')
|
|
174
|
+
continue;
|
|
175
|
+
const data = record.data;
|
|
176
|
+
const sub = data.submatches?.[0];
|
|
177
|
+
const match = {
|
|
178
|
+
path: decode(data.path),
|
|
179
|
+
line: decode(data.lines),
|
|
180
|
+
offset: Number(data.absolute_offset ?? 0),
|
|
181
|
+
needle: decode(sub?.match),
|
|
182
|
+
};
|
|
183
|
+
if (!onMatch(match)) {
|
|
184
|
+
killed = true;
|
|
185
|
+
buffer = '';
|
|
186
|
+
child.kill();
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
child.on('close', (code) => {
|
|
192
|
+
if (killed)
|
|
193
|
+
return finish();
|
|
194
|
+
// 0 = matches, 1 = no matches. 2 is "something went wrong", which with
|
|
195
|
+
// --no-messages is usually just a transcript that was deleted since the
|
|
196
|
+
// index last saw it; a real complaint (a bad pattern) reaches stderr.
|
|
197
|
+
if (code !== null && code >= 2 && stderr.trim())
|
|
198
|
+
return finish(new RipgrepError(stderr.trim()));
|
|
199
|
+
finish();
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
/** True when a usable ripgrep is on PATH. For tests and for `--help` honesty. */
|
|
204
|
+
export async function ripgrepAvailable(binary = ripgrepBinary()) {
|
|
205
|
+
try {
|
|
206
|
+
await new Promise((resolve, reject) => {
|
|
207
|
+
const child = spawn(binary, ['--version'], { stdio: 'ignore' });
|
|
208
|
+
child.on('error', reject);
|
|
209
|
+
child.on('close', (code) => code === 0 ? resolve() : reject(new Error(String(code))));
|
|
210
|
+
});
|
|
211
|
+
return true;
|
|
212
|
+
}
|
|
213
|
+
catch {
|
|
214
|
+
return false;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
//# sourceMappingURL=ripgrep.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ripgrep.js","sourceRoot":"","sources":["../src/ripgrep.ts"],"names":[],"mappings":"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"}
|
package/dist/search.d.ts
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import type { DB } from './db.js';
|
|
2
|
+
import type { ChunkKind, SearchHit, ThreadRef } from './model.js';
|
|
3
|
+
export interface FileRecord {
|
|
4
|
+
id: number;
|
|
5
|
+
path: string;
|
|
6
|
+
source: string;
|
|
7
|
+
session_uuid: string | null;
|
|
8
|
+
cwd: string | null;
|
|
9
|
+
project: string | null;
|
|
10
|
+
name: string | null;
|
|
11
|
+
parent_id: number | null;
|
|
12
|
+
lineage_id: number | null;
|
|
13
|
+
lineage_depth: number;
|
|
14
|
+
orphaned: number;
|
|
15
|
+
private: number;
|
|
16
|
+
started: string | null;
|
|
17
|
+
last_activity: string | null;
|
|
18
|
+
entry_count: number;
|
|
19
|
+
}
|
|
20
|
+
export interface SearchQuery {
|
|
21
|
+
/** FTS5 MATCH expression (see `docs/QUERIES.md`). */
|
|
22
|
+
text: string;
|
|
23
|
+
cwd?: string[];
|
|
24
|
+
project?: string[];
|
|
25
|
+
role?: string[];
|
|
26
|
+
tool?: string[];
|
|
27
|
+
kind?: ChunkKind[];
|
|
28
|
+
since?: string;
|
|
29
|
+
until?: string;
|
|
30
|
+
includePrivate?: boolean;
|
|
31
|
+
limit?: number;
|
|
32
|
+
/** Threads listed per group; the total is always reported. */
|
|
33
|
+
threadLimit?: number;
|
|
34
|
+
/** Disable the recency nudge and rank purely on bm25 x kind weight. */
|
|
35
|
+
noRecency?: boolean;
|
|
36
|
+
/** Pass the query to FTS5 verbatim, never fall back to quoting. */
|
|
37
|
+
raw?: boolean;
|
|
38
|
+
}
|
|
39
|
+
export interface SearchOutcome {
|
|
40
|
+
hits: SearchHit[];
|
|
41
|
+
/** The MATCH expression actually used, after any quoting fallback. */
|
|
42
|
+
usedQuery: string;
|
|
43
|
+
/** Total groups found before `limit` was applied. */
|
|
44
|
+
scanned: number;
|
|
45
|
+
quotedFallback: boolean;
|
|
46
|
+
}
|
|
47
|
+
export declare function loadFiles(db: DB): Map<number, FileRecord>;
|
|
48
|
+
/** Quote every token, turning a hostile string into a valid FTS5 AND query. */
|
|
49
|
+
export declare function quoteQuery(text: string): string;
|
|
50
|
+
/**
|
|
51
|
+
* Which transcripts a query is allowed to see.
|
|
52
|
+
*
|
|
53
|
+
* Visibility is decided per THREAD, because a lineage can in principle span
|
|
54
|
+
* working directories; the lineage set is only a prefilter. Both the FTS and
|
|
55
|
+
* the regex path go through here, so `private` and the cwd/project filters
|
|
56
|
+
* cannot drift apart between them.
|
|
57
|
+
*/
|
|
58
|
+
export declare function visibleSets(files: Map<number, FileRecord>, query: Pick<SearchQuery, 'cwd' | 'project' | 'includePrivate'>): {
|
|
59
|
+
files: Set<number>;
|
|
60
|
+
lineages: Set<number>;
|
|
61
|
+
totalLineages: Set<number>;
|
|
62
|
+
};
|
|
63
|
+
export declare function search(db: DB, query: SearchQuery, now?: number): SearchOutcome;
|
|
64
|
+
/**
|
|
65
|
+
* Fan one entry back out to every visible thread that carries it.
|
|
66
|
+
*
|
|
67
|
+
* Shared by the FTS and the regex path, because they must produce identical
|
|
68
|
+
* thread lists: an entry is stored once and `membership` is what turns it back
|
|
69
|
+
* into "the twelve threads that inherited this".
|
|
70
|
+
*/
|
|
71
|
+
export declare function collectThreads(entryId: number, memberStmt: ReturnType<DB['prepare']>, files: Map<number, FileRecord>, visibleFiles: Set<number>): ThreadRef[];
|
|
72
|
+
/** Resolve a user-supplied reference to a thread: file id, uuid prefix, or path. */
|
|
73
|
+
export declare function resolveThread(db: DB, ref: string): FileRecord | null;
|
|
74
|
+
export interface ThreadEntry {
|
|
75
|
+
seq: number;
|
|
76
|
+
entryId: number;
|
|
77
|
+
entryKey: string;
|
|
78
|
+
role: string;
|
|
79
|
+
tool: string | null;
|
|
80
|
+
ts: string | null;
|
|
81
|
+
/** True when this entry was inherited from an ancestor rather than written here. */
|
|
82
|
+
shared: boolean;
|
|
83
|
+
/** Transcript that first recorded this entry, and where it lives verbatim. */
|
|
84
|
+
ownerPath: string | null;
|
|
85
|
+
byteOffset: number;
|
|
86
|
+
byteLength: number;
|
|
87
|
+
texts: Array<{
|
|
88
|
+
kind: ChunkKind;
|
|
89
|
+
text: string;
|
|
90
|
+
}>;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Read one thread back in order.
|
|
94
|
+
*
|
|
95
|
+
* `ownerPath` + byte range is the pointer back to the source transcript. The
|
|
96
|
+
* index deliberately stores tool output truncated (or not at all), so anything
|
|
97
|
+
* that needs full fidelity re-reads those bytes instead of trusting the cache.
|
|
98
|
+
*/
|
|
99
|
+
export declare function readThread(db: DB, fileId: number, from?: number, limit?: number): ThreadEntry[];
|
|
100
|
+
export interface IndexStatsRow {
|
|
101
|
+
files: number;
|
|
102
|
+
lineages: number;
|
|
103
|
+
forks: number;
|
|
104
|
+
entries: number;
|
|
105
|
+
memberships: number;
|
|
106
|
+
chunks: number;
|
|
107
|
+
privateFiles: number;
|
|
108
|
+
orphans: number;
|
|
109
|
+
projects: number;
|
|
110
|
+
oldest: string | null;
|
|
111
|
+
newest: string | null;
|
|
112
|
+
}
|
|
113
|
+
export declare function indexStats(db: DB): IndexStatsRow;
|
|
114
|
+
//# sourceMappingURL=search.d.ts.map
|