@deeplake/hivemind 0.6.47 → 0.6.48

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.
@@ -0,0 +1,1190 @@
1
+ // dist/src/utils/stdin.js
2
+ function readStdin() {
3
+ return new Promise((resolve, reject) => {
4
+ let data = "";
5
+ process.stdin.setEncoding("utf-8");
6
+ process.stdin.on("data", (chunk) => data += chunk);
7
+ process.stdin.on("end", () => {
8
+ try {
9
+ resolve(JSON.parse(data));
10
+ } catch (err) {
11
+ reject(new Error(`Failed to parse hook input: ${err}`));
12
+ }
13
+ });
14
+ process.stdin.on("error", reject);
15
+ });
16
+ }
17
+
18
+ // dist/src/config.js
19
+ import { readFileSync, existsSync } from "node:fs";
20
+ import { join } from "node:path";
21
+ import { homedir, userInfo } from "node:os";
22
+ function loadConfig() {
23
+ const home = homedir();
24
+ const credPath = join(home, ".deeplake", "credentials.json");
25
+ let creds = null;
26
+ if (existsSync(credPath)) {
27
+ try {
28
+ creds = JSON.parse(readFileSync(credPath, "utf-8"));
29
+ } catch {
30
+ return null;
31
+ }
32
+ }
33
+ const token = process.env.HIVEMIND_TOKEN ?? creds?.token;
34
+ const orgId = process.env.HIVEMIND_ORG_ID ?? creds?.orgId;
35
+ if (!token || !orgId)
36
+ return null;
37
+ return {
38
+ token,
39
+ orgId,
40
+ orgName: creds?.orgName ?? orgId,
41
+ userName: creds?.userName || userInfo().username || "unknown",
42
+ workspaceId: process.env.HIVEMIND_WORKSPACE_ID ?? creds?.workspaceId ?? "default",
43
+ apiUrl: process.env.HIVEMIND_API_URL ?? creds?.apiUrl ?? "https://api.deeplake.ai",
44
+ tableName: process.env.HIVEMIND_TABLE ?? "memory",
45
+ sessionsTableName: process.env.HIVEMIND_SESSIONS_TABLE ?? "sessions",
46
+ memoryPath: process.env.HIVEMIND_MEMORY_PATH ?? join(home, ".deeplake", "memory")
47
+ };
48
+ }
49
+
50
+ // dist/src/deeplake-api.js
51
+ import { randomUUID } from "node:crypto";
52
+ import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, writeFileSync } from "node:fs";
53
+ import { join as join3 } from "node:path";
54
+ import { tmpdir } from "node:os";
55
+
56
+ // dist/src/utils/debug.js
57
+ import { appendFileSync } from "node:fs";
58
+ import { join as join2 } from "node:path";
59
+ import { homedir as homedir2 } from "node:os";
60
+ var DEBUG = process.env.HIVEMIND_DEBUG === "1";
61
+ var LOG = join2(homedir2(), ".deeplake", "hook-debug.log");
62
+ function log(tag, msg) {
63
+ if (!DEBUG)
64
+ return;
65
+ appendFileSync(LOG, `${(/* @__PURE__ */ new Date()).toISOString()} [${tag}] ${msg}
66
+ `);
67
+ }
68
+
69
+ // dist/src/utils/sql.js
70
+ function sqlStr(value) {
71
+ return value.replace(/\\/g, "\\\\").replace(/'/g, "''").replace(/\0/g, "").replace(/[\x01-\x08\x0b\x0c\x0e-\x1f\x7f]/g, "");
72
+ }
73
+ function sqlLike(value) {
74
+ return sqlStr(value).replace(/%/g, "\\%").replace(/_/g, "\\_");
75
+ }
76
+
77
+ // dist/src/deeplake-api.js
78
+ var log2 = (msg) => log("sdk", msg);
79
+ function summarizeSql(sql, maxLen = 220) {
80
+ const compact = sql.replace(/\s+/g, " ").trim();
81
+ return compact.length > maxLen ? `${compact.slice(0, maxLen)}...` : compact;
82
+ }
83
+ function traceSql(msg) {
84
+ const traceEnabled = process.env.HIVEMIND_TRACE_SQL === "1" || process.env.HIVEMIND_DEBUG === "1";
85
+ if (!traceEnabled)
86
+ return;
87
+ process.stderr.write(`[deeplake-sql] ${msg}
88
+ `);
89
+ if (process.env.HIVEMIND_DEBUG === "1")
90
+ log2(msg);
91
+ }
92
+ var RETRYABLE_CODES = /* @__PURE__ */ new Set([429, 500, 502, 503, 504]);
93
+ var MAX_RETRIES = 3;
94
+ var BASE_DELAY_MS = 500;
95
+ var MAX_CONCURRENCY = 5;
96
+ var QUERY_TIMEOUT_MS = Number(process.env.HIVEMIND_QUERY_TIMEOUT_MS ?? 1e4);
97
+ var INDEX_MARKER_TTL_MS = Number(process.env.HIVEMIND_INDEX_MARKER_TTL_MS ?? 6 * 60 * 6e4);
98
+ function sleep(ms) {
99
+ return new Promise((resolve) => setTimeout(resolve, ms));
100
+ }
101
+ function isTimeoutError(error) {
102
+ const name = error instanceof Error ? error.name.toLowerCase() : "";
103
+ const message = error instanceof Error ? error.message.toLowerCase() : String(error).toLowerCase();
104
+ return name.includes("timeout") || name === "aborterror" || message.includes("timeout") || message.includes("timed out");
105
+ }
106
+ function isDuplicateIndexError(error) {
107
+ const message = error instanceof Error ? error.message.toLowerCase() : String(error).toLowerCase();
108
+ return message.includes("duplicate key value violates unique constraint") || message.includes("pg_class_relname_nsp_index") || message.includes("already exists");
109
+ }
110
+ function isSessionInsertQuery(sql) {
111
+ return /^\s*insert\s+into\s+"[^"]+"\s*\(\s*id\s*,\s*path\s*,\s*filename\s*,\s*message\s*,/i.test(sql);
112
+ }
113
+ function isTransientHtml403(text) {
114
+ const body = text.toLowerCase();
115
+ return body.includes("<html") || body.includes("403 forbidden") || body.includes("cloudflare") || body.includes("nginx");
116
+ }
117
+ function getIndexMarkerDir() {
118
+ return process.env.HIVEMIND_INDEX_MARKER_DIR ?? join3(tmpdir(), "hivemind-deeplake-indexes");
119
+ }
120
+ var Semaphore = class {
121
+ max;
122
+ waiting = [];
123
+ active = 0;
124
+ constructor(max) {
125
+ this.max = max;
126
+ }
127
+ async acquire() {
128
+ if (this.active < this.max) {
129
+ this.active++;
130
+ return;
131
+ }
132
+ await new Promise((resolve) => this.waiting.push(resolve));
133
+ }
134
+ release() {
135
+ this.active--;
136
+ const next = this.waiting.shift();
137
+ if (next) {
138
+ this.active++;
139
+ next();
140
+ }
141
+ }
142
+ };
143
+ var DeeplakeApi = class {
144
+ token;
145
+ apiUrl;
146
+ orgId;
147
+ workspaceId;
148
+ tableName;
149
+ _pendingRows = [];
150
+ _sem = new Semaphore(MAX_CONCURRENCY);
151
+ _tablesCache = null;
152
+ constructor(token, apiUrl, orgId, workspaceId, tableName) {
153
+ this.token = token;
154
+ this.apiUrl = apiUrl;
155
+ this.orgId = orgId;
156
+ this.workspaceId = workspaceId;
157
+ this.tableName = tableName;
158
+ }
159
+ /** Execute SQL with retry on transient errors and bounded concurrency. */
160
+ async query(sql) {
161
+ const startedAt = Date.now();
162
+ const summary = summarizeSql(sql);
163
+ traceSql(`query start: ${summary}`);
164
+ await this._sem.acquire();
165
+ try {
166
+ const rows = await this._queryWithRetry(sql);
167
+ traceSql(`query ok (${Date.now() - startedAt}ms, rows=${rows.length}): ${summary}`);
168
+ return rows;
169
+ } catch (e) {
170
+ const message = e instanceof Error ? e.message : String(e);
171
+ traceSql(`query fail (${Date.now() - startedAt}ms): ${summary} :: ${message}`);
172
+ throw e;
173
+ } finally {
174
+ this._sem.release();
175
+ }
176
+ }
177
+ async _queryWithRetry(sql) {
178
+ let lastError;
179
+ for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
180
+ let resp;
181
+ try {
182
+ const signal = AbortSignal.timeout(QUERY_TIMEOUT_MS);
183
+ resp = await fetch(`${this.apiUrl}/workspaces/${this.workspaceId}/tables/query`, {
184
+ method: "POST",
185
+ headers: {
186
+ Authorization: `Bearer ${this.token}`,
187
+ "Content-Type": "application/json",
188
+ "X-Activeloop-Org-Id": this.orgId
189
+ },
190
+ signal,
191
+ body: JSON.stringify({ query: sql })
192
+ });
193
+ } catch (e) {
194
+ if (isTimeoutError(e)) {
195
+ lastError = new Error(`Query timeout after ${QUERY_TIMEOUT_MS}ms`);
196
+ throw lastError;
197
+ }
198
+ lastError = e instanceof Error ? e : new Error(String(e));
199
+ if (attempt < MAX_RETRIES) {
200
+ const delay = BASE_DELAY_MS * Math.pow(2, attempt) + Math.random() * 200;
201
+ log2(`query retry ${attempt + 1}/${MAX_RETRIES} (fetch error: ${lastError.message}) in ${delay.toFixed(0)}ms`);
202
+ await sleep(delay);
203
+ continue;
204
+ }
205
+ throw lastError;
206
+ }
207
+ if (resp.ok) {
208
+ const raw = await resp.json();
209
+ if (!raw?.rows || !raw?.columns)
210
+ return [];
211
+ return raw.rows.map((row) => Object.fromEntries(raw.columns.map((col, i) => [col, row[i]])));
212
+ }
213
+ const text = await resp.text().catch(() => "");
214
+ const retryable403 = isSessionInsertQuery(sql) && (resp.status === 401 || resp.status === 403 && (text.length === 0 || isTransientHtml403(text)));
215
+ if (attempt < MAX_RETRIES && (RETRYABLE_CODES.has(resp.status) || retryable403)) {
216
+ const delay = BASE_DELAY_MS * Math.pow(2, attempt) + Math.random() * 200;
217
+ log2(`query retry ${attempt + 1}/${MAX_RETRIES} (${resp.status}) in ${delay.toFixed(0)}ms`);
218
+ await sleep(delay);
219
+ continue;
220
+ }
221
+ throw new Error(`Query failed: ${resp.status}: ${text.slice(0, 200)}`);
222
+ }
223
+ throw lastError ?? new Error("Query failed: max retries exceeded");
224
+ }
225
+ // ── Writes ──────────────────────────────────────────────────────────────────
226
+ /** Queue rows for writing. Call commit() to flush. */
227
+ appendRows(rows) {
228
+ this._pendingRows.push(...rows);
229
+ }
230
+ /** Flush pending rows via SQL. */
231
+ async commit() {
232
+ if (this._pendingRows.length === 0)
233
+ return;
234
+ const rows = this._pendingRows;
235
+ this._pendingRows = [];
236
+ const CONCURRENCY = 10;
237
+ for (let i = 0; i < rows.length; i += CONCURRENCY) {
238
+ const chunk = rows.slice(i, i + CONCURRENCY);
239
+ await Promise.allSettled(chunk.map((r) => this.upsertRowSql(r)));
240
+ }
241
+ log2(`commit: ${rows.length} rows`);
242
+ }
243
+ async upsertRowSql(row) {
244
+ const ts = (/* @__PURE__ */ new Date()).toISOString();
245
+ const cd = row.creationDate ?? ts;
246
+ const lud = row.lastUpdateDate ?? ts;
247
+ const exists = await this.query(`SELECT path FROM "${this.tableName}" WHERE path = '${sqlStr(row.path)}' LIMIT 1`);
248
+ if (exists.length > 0) {
249
+ let setClauses = `summary = E'${sqlStr(row.contentText)}', mime_type = '${sqlStr(row.mimeType)}', size_bytes = ${row.sizeBytes}, last_update_date = '${lud}'`;
250
+ if (row.project !== void 0)
251
+ setClauses += `, project = '${sqlStr(row.project)}'`;
252
+ if (row.description !== void 0)
253
+ setClauses += `, description = '${sqlStr(row.description)}'`;
254
+ await this.query(`UPDATE "${this.tableName}" SET ${setClauses} WHERE path = '${sqlStr(row.path)}'`);
255
+ } else {
256
+ const id = randomUUID();
257
+ let cols = "id, path, filename, summary, mime_type, size_bytes, creation_date, last_update_date";
258
+ let vals = `'${id}', '${sqlStr(row.path)}', '${sqlStr(row.filename)}', E'${sqlStr(row.contentText)}', '${sqlStr(row.mimeType)}', ${row.sizeBytes}, '${cd}', '${lud}'`;
259
+ if (row.project !== void 0) {
260
+ cols += ", project";
261
+ vals += `, '${sqlStr(row.project)}'`;
262
+ }
263
+ if (row.description !== void 0) {
264
+ cols += ", description";
265
+ vals += `, '${sqlStr(row.description)}'`;
266
+ }
267
+ await this.query(`INSERT INTO "${this.tableName}" (${cols}) VALUES (${vals})`);
268
+ }
269
+ }
270
+ /** Update specific columns on a row by path. */
271
+ async updateColumns(path, columns) {
272
+ const setClauses = Object.entries(columns).map(([col, val]) => typeof val === "number" ? `${col} = ${val}` : `${col} = '${sqlStr(String(val))}'`).join(", ");
273
+ await this.query(`UPDATE "${this.tableName}" SET ${setClauses} WHERE path = '${sqlStr(path)}'`);
274
+ }
275
+ // ── Convenience ─────────────────────────────────────────────────────────────
276
+ /** Create a BM25 search index on a column. */
277
+ async createIndex(column) {
278
+ await this.query(`CREATE INDEX IF NOT EXISTS idx_${sqlStr(column)}_bm25 ON "${this.tableName}" USING deeplake_index ("${column}")`);
279
+ }
280
+ buildLookupIndexName(table, suffix) {
281
+ return `idx_${table}_${suffix}`.replace(/[^a-zA-Z0-9_]/g, "_");
282
+ }
283
+ getLookupIndexMarkerPath(table, suffix) {
284
+ const markerKey = [
285
+ this.workspaceId,
286
+ this.orgId,
287
+ table,
288
+ suffix
289
+ ].join("__").replace(/[^a-zA-Z0-9_.-]/g, "_");
290
+ return join3(getIndexMarkerDir(), `${markerKey}.json`);
291
+ }
292
+ hasFreshLookupIndexMarker(table, suffix) {
293
+ const markerPath = this.getLookupIndexMarkerPath(table, suffix);
294
+ if (!existsSync2(markerPath))
295
+ return false;
296
+ try {
297
+ const raw = JSON.parse(readFileSync2(markerPath, "utf-8"));
298
+ const updatedAt = raw.updatedAt ? new Date(raw.updatedAt).getTime() : NaN;
299
+ if (!Number.isFinite(updatedAt) || Date.now() - updatedAt > INDEX_MARKER_TTL_MS)
300
+ return false;
301
+ return true;
302
+ } catch {
303
+ return false;
304
+ }
305
+ }
306
+ markLookupIndexReady(table, suffix) {
307
+ mkdirSync(getIndexMarkerDir(), { recursive: true });
308
+ writeFileSync(this.getLookupIndexMarkerPath(table, suffix), JSON.stringify({ updatedAt: (/* @__PURE__ */ new Date()).toISOString() }), "utf-8");
309
+ }
310
+ async ensureLookupIndex(table, suffix, columnsSql) {
311
+ if (this.hasFreshLookupIndexMarker(table, suffix))
312
+ return;
313
+ const indexName = this.buildLookupIndexName(table, suffix);
314
+ try {
315
+ await this.query(`CREATE INDEX IF NOT EXISTS "${indexName}" ON "${table}" ${columnsSql}`);
316
+ this.markLookupIndexReady(table, suffix);
317
+ } catch (e) {
318
+ if (isDuplicateIndexError(e)) {
319
+ this.markLookupIndexReady(table, suffix);
320
+ return;
321
+ }
322
+ log2(`index "${indexName}" skipped: ${e.message}`);
323
+ }
324
+ }
325
+ /** List all tables in the workspace (with retry). */
326
+ async listTables(forceRefresh = false) {
327
+ if (!forceRefresh && this._tablesCache)
328
+ return [...this._tablesCache];
329
+ const { tables, cacheable } = await this._fetchTables();
330
+ if (cacheable)
331
+ this._tablesCache = [...tables];
332
+ return tables;
333
+ }
334
+ async _fetchTables() {
335
+ for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
336
+ try {
337
+ const resp = await fetch(`${this.apiUrl}/workspaces/${this.workspaceId}/tables`, {
338
+ headers: {
339
+ Authorization: `Bearer ${this.token}`,
340
+ "X-Activeloop-Org-Id": this.orgId
341
+ }
342
+ });
343
+ if (resp.ok) {
344
+ const data = await resp.json();
345
+ return {
346
+ tables: (data.tables ?? []).map((t) => t.table_name),
347
+ cacheable: true
348
+ };
349
+ }
350
+ if (attempt < MAX_RETRIES && RETRYABLE_CODES.has(resp.status)) {
351
+ await sleep(BASE_DELAY_MS * Math.pow(2, attempt) + Math.random() * 200);
352
+ continue;
353
+ }
354
+ return { tables: [], cacheable: false };
355
+ } catch {
356
+ if (attempt < MAX_RETRIES) {
357
+ await sleep(BASE_DELAY_MS * Math.pow(2, attempt));
358
+ continue;
359
+ }
360
+ return { tables: [], cacheable: false };
361
+ }
362
+ }
363
+ return { tables: [], cacheable: false };
364
+ }
365
+ /** Create the memory table if it doesn't already exist. Migrate columns on existing tables. */
366
+ async ensureTable(name) {
367
+ const tbl = name ?? this.tableName;
368
+ const tables = await this.listTables();
369
+ if (!tables.includes(tbl)) {
370
+ log2(`table "${tbl}" not found, creating`);
371
+ await this.query(`CREATE TABLE IF NOT EXISTS "${tbl}" (id TEXT NOT NULL DEFAULT '', path TEXT NOT NULL DEFAULT '', filename TEXT NOT NULL DEFAULT '', summary TEXT NOT NULL DEFAULT '', author TEXT NOT NULL DEFAULT '', mime_type TEXT NOT NULL DEFAULT 'text/plain', size_bytes BIGINT NOT NULL DEFAULT 0, project TEXT NOT NULL DEFAULT '', description TEXT NOT NULL DEFAULT '', agent TEXT NOT NULL DEFAULT '', creation_date TEXT NOT NULL DEFAULT '', last_update_date TEXT NOT NULL DEFAULT '') USING deeplake`);
372
+ log2(`table "${tbl}" created`);
373
+ if (!tables.includes(tbl))
374
+ this._tablesCache = [...tables, tbl];
375
+ }
376
+ }
377
+ /** Create the sessions table (uses JSONB for message since every row is a JSON event). */
378
+ async ensureSessionsTable(name) {
379
+ const tables = await this.listTables();
380
+ if (!tables.includes(name)) {
381
+ log2(`table "${name}" not found, creating`);
382
+ await this.query(`CREATE TABLE IF NOT EXISTS "${name}" (id TEXT NOT NULL DEFAULT '', path TEXT NOT NULL DEFAULT '', filename TEXT NOT NULL DEFAULT '', message JSONB, author TEXT NOT NULL DEFAULT '', mime_type TEXT NOT NULL DEFAULT 'application/json', size_bytes BIGINT NOT NULL DEFAULT 0, project TEXT NOT NULL DEFAULT '', description TEXT NOT NULL DEFAULT '', agent TEXT NOT NULL DEFAULT '', creation_date TEXT NOT NULL DEFAULT '', last_update_date TEXT NOT NULL DEFAULT '') USING deeplake`);
383
+ log2(`table "${name}" created`);
384
+ if (!tables.includes(name))
385
+ this._tablesCache = [...tables, name];
386
+ }
387
+ await this.ensureLookupIndex(name, "path_creation_date", `("path", "creation_date")`);
388
+ }
389
+ };
390
+
391
+ // dist/src/shell/grep-core.js
392
+ var TOOL_INPUT_FIELDS = [
393
+ "command",
394
+ "file_path",
395
+ "path",
396
+ "pattern",
397
+ "prompt",
398
+ "subagent_type",
399
+ "query",
400
+ "url",
401
+ "notebook_path",
402
+ "old_string",
403
+ "new_string",
404
+ "content",
405
+ "skill",
406
+ "args",
407
+ "taskId",
408
+ "status",
409
+ "subject",
410
+ "description",
411
+ "to",
412
+ "message",
413
+ "summary",
414
+ "max_results"
415
+ ];
416
+ var TOOL_RESPONSE_DROP = /* @__PURE__ */ new Set([
417
+ // Note: `stderr` is intentionally NOT in this set. The `stdout` high-signal
418
+ // branch below already de-dupes it for the common case (appends as suffix
419
+ // when non-empty). If a tool response has ONLY `stderr` and no `stdout`
420
+ // (hard-failure on some tools), the generic cleanup preserves it so the
421
+ // error message reaches Claude instead of collapsing to `[ok]`.
422
+ "interrupted",
423
+ "isImage",
424
+ "noOutputExpected",
425
+ "type",
426
+ "structuredPatch",
427
+ "userModified",
428
+ "originalFile",
429
+ "replaceAll",
430
+ "totalDurationMs",
431
+ "totalTokens",
432
+ "totalToolUseCount",
433
+ "usage",
434
+ "toolStats",
435
+ "durationMs",
436
+ "durationSeconds",
437
+ "bytes",
438
+ "code",
439
+ "codeText",
440
+ "agentId",
441
+ "agentType",
442
+ "verificationNudgeNeeded",
443
+ "numLines",
444
+ "numFiles",
445
+ "truncated",
446
+ "statusChange",
447
+ "updatedFields",
448
+ "isAgent",
449
+ "success"
450
+ ]);
451
+ function maybeParseJson(v) {
452
+ if (typeof v !== "string")
453
+ return v;
454
+ const s = v.trim();
455
+ if (s[0] !== "{" && s[0] !== "[")
456
+ return v;
457
+ try {
458
+ return JSON.parse(s);
459
+ } catch {
460
+ return v;
461
+ }
462
+ }
463
+ function snakeCase(k) {
464
+ return k.replace(/([A-Z])/g, "_$1").toLowerCase();
465
+ }
466
+ function camelCase(k) {
467
+ return k.replace(/_([a-z])/g, (_, c) => c.toUpperCase());
468
+ }
469
+ function formatToolInput(raw) {
470
+ const p = maybeParseJson(raw);
471
+ if (typeof p !== "object" || p === null)
472
+ return String(p ?? "");
473
+ const parts = [];
474
+ for (const k of TOOL_INPUT_FIELDS) {
475
+ if (p[k] === void 0)
476
+ continue;
477
+ const v = p[k];
478
+ parts.push(`${k}: ${typeof v === "string" ? v : JSON.stringify(v)}`);
479
+ }
480
+ for (const k of ["glob", "output_mode", "limit", "offset"]) {
481
+ if (p[k] !== void 0)
482
+ parts.push(`${k}: ${p[k]}`);
483
+ }
484
+ return parts.length ? parts.join("\n") : JSON.stringify(p);
485
+ }
486
+ function formatToolResponse(raw, inp, toolName) {
487
+ const r = maybeParseJson(raw);
488
+ if (typeof r !== "object" || r === null)
489
+ return String(r ?? "");
490
+ if (toolName === "Edit" || toolName === "Write" || toolName === "MultiEdit") {
491
+ return r.filePath ? `[wrote ${r.filePath}]` : "[ok]";
492
+ }
493
+ if (typeof r.stdout === "string") {
494
+ const stderr = r.stderr;
495
+ return r.stdout + (stderr ? `
496
+ stderr: ${stderr}` : "");
497
+ }
498
+ if (typeof r.content === "string")
499
+ return r.content;
500
+ if (r.file && typeof r.file === "object") {
501
+ const f = r.file;
502
+ if (typeof f.content === "string")
503
+ return `[${f.filePath ?? ""}]
504
+ ${f.content}`;
505
+ if (typeof f.base64 === "string")
506
+ return `[binary ${f.filePath ?? ""}: ${f.base64.length} base64 chars]`;
507
+ }
508
+ if (Array.isArray(r.filenames))
509
+ return r.filenames.join("\n");
510
+ if (Array.isArray(r.matches)) {
511
+ return r.matches.map((m) => typeof m === "string" ? m : JSON.stringify(m)).join("\n");
512
+ }
513
+ if (Array.isArray(r.results)) {
514
+ return r.results.map((x) => typeof x === "string" ? x : x?.title ?? x?.url ?? JSON.stringify(x)).join("\n");
515
+ }
516
+ const inpObj = maybeParseJson(inp);
517
+ const kept = {};
518
+ for (const [k, v] of Object.entries(r)) {
519
+ if (TOOL_RESPONSE_DROP.has(k))
520
+ continue;
521
+ if (v === "" || v === false || v == null)
522
+ continue;
523
+ if (typeof inpObj === "object" && inpObj) {
524
+ const inObj = inpObj;
525
+ if (k in inObj && JSON.stringify(inObj[k]) === JSON.stringify(v))
526
+ continue;
527
+ const snake = snakeCase(k);
528
+ if (snake in inObj && JSON.stringify(inObj[snake]) === JSON.stringify(v))
529
+ continue;
530
+ const camel = camelCase(k);
531
+ if (camel in inObj && JSON.stringify(inObj[camel]) === JSON.stringify(v))
532
+ continue;
533
+ }
534
+ kept[k] = v;
535
+ }
536
+ return Object.keys(kept).length ? JSON.stringify(kept) : "[ok]";
537
+ }
538
+ function formatToolCall(obj) {
539
+ return `[tool:${obj?.tool_name ?? "?"}]
540
+ input: ${formatToolInput(obj?.tool_input)}
541
+ response: ${formatToolResponse(obj?.tool_response, obj?.tool_input, obj?.tool_name)}`;
542
+ }
543
+ function normalizeContent(path, raw) {
544
+ if (!path.includes("/sessions/"))
545
+ return raw;
546
+ if (!raw || raw[0] !== "{")
547
+ return raw;
548
+ let obj;
549
+ try {
550
+ obj = JSON.parse(raw);
551
+ } catch {
552
+ return raw;
553
+ }
554
+ if (Array.isArray(obj.turns)) {
555
+ const header = [];
556
+ if (obj.date_time)
557
+ header.push(`date: ${obj.date_time}`);
558
+ if (obj.speakers) {
559
+ const s = obj.speakers;
560
+ const names = [s.speaker_a, s.speaker_b].filter(Boolean).join(", ");
561
+ if (names)
562
+ header.push(`speakers: ${names}`);
563
+ }
564
+ const lines = obj.turns.map((t) => {
565
+ const sp = String(t?.speaker ?? t?.name ?? "?").trim();
566
+ const tx = String(t?.text ?? t?.content ?? "").replace(/\s+/g, " ").trim();
567
+ const tag = t?.dia_id ? `[${t.dia_id}] ` : "";
568
+ return `${tag}${sp}: ${tx}`;
569
+ });
570
+ const out2 = [...header, ...lines].join("\n");
571
+ return out2.trim() ? out2 : raw;
572
+ }
573
+ const stripRecalled = (t) => {
574
+ const i = t.indexOf("<recalled-memories>");
575
+ if (i === -1)
576
+ return t;
577
+ const j = t.lastIndexOf("</recalled-memories>");
578
+ if (j === -1 || j < i)
579
+ return t;
580
+ const head = t.slice(0, i);
581
+ const tail = t.slice(j + "</recalled-memories>".length);
582
+ return (head + tail).replace(/^\s+/, "").replace(/\n{3,}/g, "\n\n");
583
+ };
584
+ let out = null;
585
+ if (obj.type === "user_message") {
586
+ out = `[user] ${stripRecalled(String(obj.content ?? ""))}`;
587
+ } else if (obj.type === "assistant_message") {
588
+ const agent = obj.agent_type ? ` (agent=${obj.agent_type})` : "";
589
+ out = `[assistant${agent}] ${stripRecalled(String(obj.content ?? ""))}`;
590
+ } else if (obj.type === "tool_call") {
591
+ out = formatToolCall(obj);
592
+ }
593
+ if (out === null)
594
+ return raw;
595
+ const trimmed = out.trim();
596
+ if (!trimmed || trimmed === "[user]" || trimmed === "[assistant]" || /^\[tool:[^\]]*\]\s+input:\s+\{\}\s+response:\s+\{\}$/.test(trimmed))
597
+ return raw;
598
+ return out;
599
+ }
600
+ function buildPathCondition(targetPath) {
601
+ if (!targetPath || targetPath === "/")
602
+ return "";
603
+ const clean = targetPath.replace(/\/+$/, "");
604
+ if (/[*?]/.test(clean)) {
605
+ const likePattern = sqlLike(clean).replace(/\*/g, "%").replace(/\?/g, "_");
606
+ return `path LIKE '${likePattern}' ESCAPE '\\'`;
607
+ }
608
+ const base = clean.split("/").pop() ?? "";
609
+ if (base.includes(".")) {
610
+ return `path = '${sqlStr(clean)}'`;
611
+ }
612
+ return `(path = '${sqlStr(clean)}' OR path LIKE '${sqlLike(clean)}/%' ESCAPE '\\')`;
613
+ }
614
+ async function searchDeeplakeTables(api, memoryTable, sessionsTable, opts) {
615
+ const { pathFilter, contentScanOnly, likeOp, escapedPattern, prefilterPattern, prefilterPatterns, multiWordPatterns } = opts;
616
+ const limit = opts.limit ?? 100;
617
+ const filterPatterns = contentScanOnly ? prefilterPatterns && prefilterPatterns.length > 0 ? prefilterPatterns : prefilterPattern ? [prefilterPattern] : [] : multiWordPatterns && multiWordPatterns.length > 1 ? multiWordPatterns : [escapedPattern];
618
+ const memFilter = buildContentFilter("summary::text", likeOp, filterPatterns);
619
+ const sessFilter = buildContentFilter("message::text", likeOp, filterPatterns);
620
+ const memQuery = `SELECT path, summary::text AS content, 0 AS source_order, '' AS creation_date FROM "${memoryTable}" WHERE 1=1${pathFilter}${memFilter} LIMIT ${limit}`;
621
+ const sessQuery = `SELECT path, message::text AS content, 1 AS source_order, COALESCE(creation_date::text, '') AS creation_date FROM "${sessionsTable}" WHERE 1=1${pathFilter}${sessFilter} LIMIT ${limit}`;
622
+ const rows = await api.query(`SELECT path, content, source_order, creation_date FROM ((${memQuery}) UNION ALL (${sessQuery})) AS combined ORDER BY path, source_order, creation_date`);
623
+ return rows.map((row) => ({
624
+ path: String(row["path"]),
625
+ content: String(row["content"] ?? "")
626
+ }));
627
+ }
628
+ function buildPathFilter(targetPath) {
629
+ const condition = buildPathCondition(targetPath);
630
+ return condition ? ` AND ${condition}` : "";
631
+ }
632
+ function extractRegexLiteralPrefilter(pattern) {
633
+ if (!pattern)
634
+ return null;
635
+ const parts = [];
636
+ let current = "";
637
+ for (let i = 0; i < pattern.length; i++) {
638
+ const ch = pattern[i];
639
+ if (ch === "\\") {
640
+ const next = pattern[i + 1];
641
+ if (!next)
642
+ return null;
643
+ if (/[dDsSwWbBAZzGkKpP]/.test(next))
644
+ return null;
645
+ current += next;
646
+ i++;
647
+ continue;
648
+ }
649
+ if (ch === ".") {
650
+ if (pattern[i + 1] === "*") {
651
+ if (current)
652
+ parts.push(current);
653
+ current = "";
654
+ i++;
655
+ continue;
656
+ }
657
+ return null;
658
+ }
659
+ if ("|()[]{}+?^$".includes(ch) || ch === "*")
660
+ return null;
661
+ current += ch;
662
+ }
663
+ if (current)
664
+ parts.push(current);
665
+ const literal = parts.reduce((best, part) => part.length > best.length ? part : best, "");
666
+ return literal.length >= 2 ? literal : null;
667
+ }
668
+ function extractRegexAlternationPrefilters(pattern) {
669
+ if (!pattern.includes("|"))
670
+ return null;
671
+ const parts = [];
672
+ let current = "";
673
+ let escaped = false;
674
+ for (let i = 0; i < pattern.length; i++) {
675
+ const ch = pattern[i];
676
+ if (escaped) {
677
+ current += `\\${ch}`;
678
+ escaped = false;
679
+ continue;
680
+ }
681
+ if (ch === "\\") {
682
+ escaped = true;
683
+ continue;
684
+ }
685
+ if (ch === "|") {
686
+ if (!current)
687
+ return null;
688
+ parts.push(current);
689
+ current = "";
690
+ continue;
691
+ }
692
+ if ("()[]{}^$".includes(ch))
693
+ return null;
694
+ current += ch;
695
+ }
696
+ if (escaped || !current)
697
+ return null;
698
+ parts.push(current);
699
+ const literals = [...new Set(parts.map((part) => extractRegexLiteralPrefilter(part)).filter((part) => typeof part === "string" && part.length >= 2))];
700
+ return literals.length > 0 ? literals : null;
701
+ }
702
+ function buildGrepSearchOptions(params, targetPath) {
703
+ const hasRegexMeta = !params.fixedString && /[.*+?^${}()|[\]\\]/.test(params.pattern);
704
+ const literalPrefilter = hasRegexMeta ? extractRegexLiteralPrefilter(params.pattern) : null;
705
+ const alternationPrefilters = hasRegexMeta ? extractRegexAlternationPrefilters(params.pattern) : null;
706
+ const multiWordPatterns = !hasRegexMeta ? params.pattern.split(/\s+/).filter((w) => w.length > 2).slice(0, 4) : [];
707
+ return {
708
+ pathFilter: buildPathFilter(targetPath),
709
+ contentScanOnly: hasRegexMeta,
710
+ likeOp: params.ignoreCase ? "ILIKE" : "LIKE",
711
+ escapedPattern: sqlLike(params.pattern),
712
+ prefilterPattern: literalPrefilter ? sqlLike(literalPrefilter) : void 0,
713
+ prefilterPatterns: alternationPrefilters?.map((literal) => sqlLike(literal)),
714
+ multiWordPatterns: multiWordPatterns.length > 1 ? multiWordPatterns.map((w) => sqlLike(w)) : void 0
715
+ };
716
+ }
717
+ function buildContentFilter(column, likeOp, patterns) {
718
+ if (patterns.length === 0)
719
+ return "";
720
+ if (patterns.length === 1)
721
+ return ` AND ${column} ${likeOp} '%${patterns[0]}%'`;
722
+ return ` AND (${patterns.map((pattern) => `${column} ${likeOp} '%${pattern}%'`).join(" OR ")})`;
723
+ }
724
+ function compileGrepRegex(params) {
725
+ let reStr = params.fixedString ? params.pattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") : params.pattern;
726
+ if (params.wordMatch)
727
+ reStr = `\\b${reStr}\\b`;
728
+ try {
729
+ return new RegExp(reStr, params.ignoreCase ? "i" : "");
730
+ } catch {
731
+ return new RegExp(params.pattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), params.ignoreCase ? "i" : "");
732
+ }
733
+ }
734
+ function refineGrepMatches(rows, params, forceMultiFilePrefix) {
735
+ const re = compileGrepRegex(params);
736
+ const multi = forceMultiFilePrefix ?? rows.length > 1;
737
+ const output = [];
738
+ for (const row of rows) {
739
+ if (!row.content)
740
+ continue;
741
+ const lines = row.content.split("\n");
742
+ const matched = [];
743
+ for (let i = 0; i < lines.length; i++) {
744
+ const hit = re.test(lines[i]);
745
+ if (hit !== !!params.invertMatch) {
746
+ if (params.filesOnly) {
747
+ output.push(row.path);
748
+ break;
749
+ }
750
+ const prefix = multi ? `${row.path}:` : "";
751
+ const ln = params.lineNumber ? `${i + 1}:` : "";
752
+ matched.push(`${prefix}${ln}${lines[i]}`);
753
+ }
754
+ }
755
+ if (!params.filesOnly) {
756
+ if (params.countOnly) {
757
+ output.push(`${multi ? `${row.path}:` : ""}${matched.length}`);
758
+ } else {
759
+ output.push(...matched);
760
+ }
761
+ }
762
+ }
763
+ return output;
764
+ }
765
+ async function grepBothTables(api, memoryTable, sessionsTable, params, targetPath) {
766
+ const rows = await searchDeeplakeTables(api, memoryTable, sessionsTable, buildGrepSearchOptions(params, targetPath));
767
+ const seen = /* @__PURE__ */ new Set();
768
+ const unique = rows.filter((r) => seen.has(r.path) ? false : (seen.add(r.path), true));
769
+ const normalized = unique.map((r) => ({ path: r.path, content: normalizeContent(r.path, r.content) }));
770
+ return refineGrepMatches(normalized, params);
771
+ }
772
+
773
+ // dist/src/utils/output-cap.js
774
+ var CLAUDE_OUTPUT_CAP_BYTES = 8 * 1024;
775
+ function byteLen(str) {
776
+ return Buffer.byteLength(str, "utf8");
777
+ }
778
+ function capOutputForClaude(output, options = {}) {
779
+ const maxBytes = options.maxBytes ?? CLAUDE_OUTPUT_CAP_BYTES;
780
+ if (byteLen(output) <= maxBytes)
781
+ return output;
782
+ const kind = options.kind ?? "output";
783
+ const footerReserve = 220;
784
+ const budget = Math.max(1, maxBytes - footerReserve);
785
+ let running = 0;
786
+ const lines = output.split("\n");
787
+ const keptLines = [];
788
+ for (const line of lines) {
789
+ const lineBytes = byteLen(line) + 1;
790
+ if (running + lineBytes > budget)
791
+ break;
792
+ keptLines.push(line);
793
+ running += lineBytes;
794
+ }
795
+ if (keptLines.length === 0) {
796
+ const buf = Buffer.from(output, "utf8");
797
+ let cutByte = Math.min(budget, buf.length);
798
+ while (cutByte > 0 && (buf[cutByte] & 192) === 128)
799
+ cutByte--;
800
+ const slice = buf.subarray(0, cutByte).toString("utf8");
801
+ const footer2 = `
802
+ ... [${kind} truncated: ${(byteLen(output) / 1024).toFixed(1)} KB total; refine with '| head -N' or a tighter pattern]`;
803
+ return slice + footer2;
804
+ }
805
+ const totalLines = lines.length - (lines[lines.length - 1] === "" ? 1 : 0);
806
+ const elidedLines = Math.max(0, totalLines - keptLines.length);
807
+ const elidedBytes = byteLen(output) - byteLen(keptLines.join("\n"));
808
+ const footer = `
809
+ ... [${kind} truncated: ${elidedLines} more lines (${(elidedBytes / 1024).toFixed(1)} KB) elided \u2014 refine with '| head -N' or a tighter pattern]`;
810
+ return keptLines.join("\n") + footer;
811
+ }
812
+
813
+ // dist/src/hooks/grep-direct.js
814
+ function splitFirstPipelineStage(cmd) {
815
+ const input = cmd.trim();
816
+ let quote = null;
817
+ let escaped = false;
818
+ for (let i = 0; i < input.length; i++) {
819
+ const ch = input[i];
820
+ if (escaped) {
821
+ escaped = false;
822
+ continue;
823
+ }
824
+ if (quote) {
825
+ if (ch === quote) {
826
+ quote = null;
827
+ continue;
828
+ }
829
+ if (ch === "\\" && quote === '"') {
830
+ escaped = true;
831
+ }
832
+ continue;
833
+ }
834
+ if (ch === "\\") {
835
+ escaped = true;
836
+ continue;
837
+ }
838
+ if (ch === "'" || ch === '"') {
839
+ quote = ch;
840
+ continue;
841
+ }
842
+ if (ch === "|")
843
+ return input.slice(0, i).trim();
844
+ }
845
+ return quote ? null : input;
846
+ }
847
+ function tokenizeGrepStage(input) {
848
+ const tokens = [];
849
+ let current = "";
850
+ let quote = null;
851
+ for (let i = 0; i < input.length; i++) {
852
+ const ch = input[i];
853
+ if (quote) {
854
+ if (ch === quote) {
855
+ quote = null;
856
+ } else if (ch === "\\" && quote === '"' && i + 1 < input.length) {
857
+ current += input[++i];
858
+ } else {
859
+ current += ch;
860
+ }
861
+ continue;
862
+ }
863
+ if (ch === "'" || ch === '"') {
864
+ quote = ch;
865
+ continue;
866
+ }
867
+ if (ch === "\\" && i + 1 < input.length) {
868
+ current += input[++i];
869
+ continue;
870
+ }
871
+ if (/\s/.test(ch)) {
872
+ if (current) {
873
+ tokens.push(current);
874
+ current = "";
875
+ }
876
+ continue;
877
+ }
878
+ current += ch;
879
+ }
880
+ if (quote)
881
+ return null;
882
+ if (current)
883
+ tokens.push(current);
884
+ return tokens;
885
+ }
886
+ function parseBashGrep(cmd) {
887
+ const first = splitFirstPipelineStage(cmd);
888
+ if (!first)
889
+ return null;
890
+ const matchTool = first.match(/^(grep|egrep|fgrep|rg)\b/);
891
+ if (!matchTool)
892
+ return null;
893
+ const tool = matchTool[1];
894
+ const isFixed = tool === "fgrep";
895
+ const isRg = tool === "rg";
896
+ const tokens = tokenizeGrepStage(first);
897
+ if (!tokens || tokens.length === 0)
898
+ return null;
899
+ let ignoreCase = false, wordMatch = false, filesOnly = false, countOnly = false, lineNumber = isRg, invertMatch = false, fixedString = isFixed;
900
+ const explicitPatterns = [];
901
+ let ti = 1;
902
+ while (ti < tokens.length) {
903
+ const token = tokens[ti];
904
+ if (token === "--") {
905
+ ti++;
906
+ break;
907
+ }
908
+ if (!token.startsWith("-") || token === "-")
909
+ break;
910
+ if (token.startsWith("--")) {
911
+ const [flag, inlineValue] = token.split("=", 2);
912
+ const rgValueLongs = /* @__PURE__ */ new Set([
913
+ "--type",
914
+ "--type-not",
915
+ "--type-add",
916
+ "--type-clear",
917
+ "--glob",
918
+ "--iglob",
919
+ "--threads",
920
+ "--max-columns",
921
+ "--max-depth",
922
+ "--max-filesize",
923
+ "--pre",
924
+ "--pre-glob",
925
+ "--replace",
926
+ "--encoding",
927
+ "--color",
928
+ "--colors",
929
+ "--sort",
930
+ "--sortr",
931
+ "--context-separator",
932
+ "--field-context-separator",
933
+ "--field-match-separator",
934
+ "--path-separator",
935
+ "--hostname-bin"
936
+ ]);
937
+ const handlers = {
938
+ "--ignore-case": () => {
939
+ ignoreCase = true;
940
+ return false;
941
+ },
942
+ "--word-regexp": () => {
943
+ wordMatch = true;
944
+ return false;
945
+ },
946
+ "--files-with-matches": () => {
947
+ filesOnly = true;
948
+ return false;
949
+ },
950
+ // rg uses `--files` to list files (without searching). For our purposes
951
+ // it's similar enough to `-l` that we treat it as filesOnly.
952
+ "--files": () => {
953
+ filesOnly = true;
954
+ return false;
955
+ },
956
+ "--count": () => {
957
+ countOnly = true;
958
+ return false;
959
+ },
960
+ "--count-matches": () => {
961
+ countOnly = true;
962
+ return false;
963
+ },
964
+ "--line-number": () => {
965
+ lineNumber = true;
966
+ return false;
967
+ },
968
+ "--no-line-number": () => {
969
+ lineNumber = false;
970
+ return false;
971
+ },
972
+ "--invert-match": () => {
973
+ invertMatch = true;
974
+ return false;
975
+ },
976
+ "--fixed-strings": () => {
977
+ fixedString = true;
978
+ return false;
979
+ },
980
+ "--after-context": () => inlineValue === void 0,
981
+ "--before-context": () => inlineValue === void 0,
982
+ "--context": () => inlineValue === void 0,
983
+ "--max-count": () => inlineValue === void 0,
984
+ "--regexp": () => {
985
+ if (inlineValue !== void 0) {
986
+ explicitPatterns.push(inlineValue);
987
+ return false;
988
+ }
989
+ return true;
990
+ }
991
+ };
992
+ let consumeNext = handlers[flag]?.() ?? false;
993
+ if (!consumeNext && isRg && rgValueLongs.has(flag) && inlineValue === void 0) {
994
+ consumeNext = true;
995
+ }
996
+ if (consumeNext) {
997
+ ti++;
998
+ if (ti >= tokens.length)
999
+ return null;
1000
+ if (flag === "--regexp")
1001
+ explicitPatterns.push(tokens[ti]);
1002
+ }
1003
+ ti++;
1004
+ continue;
1005
+ }
1006
+ const rgValueShorts = new Set(isRg ? ["t", "T", "g", "j", "M", "r", "E"] : []);
1007
+ const shortFlags = token.slice(1);
1008
+ let consumedValueFlag = false;
1009
+ for (let i = 0; i < shortFlags.length; i++) {
1010
+ const flag = shortFlags[i];
1011
+ switch (flag) {
1012
+ case "i":
1013
+ ignoreCase = true;
1014
+ break;
1015
+ case "w":
1016
+ wordMatch = true;
1017
+ break;
1018
+ case "l":
1019
+ filesOnly = true;
1020
+ break;
1021
+ case "c":
1022
+ countOnly = true;
1023
+ break;
1024
+ case "n":
1025
+ lineNumber = true;
1026
+ break;
1027
+ case "N":
1028
+ lineNumber = false;
1029
+ break;
1030
+ // rg --no-line-number short form
1031
+ case "v":
1032
+ invertMatch = true;
1033
+ break;
1034
+ case "F":
1035
+ fixedString = true;
1036
+ break;
1037
+ case "r":
1038
+ if (isRg) {
1039
+ if (i === shortFlags.length - 1) {
1040
+ ti++;
1041
+ if (ti >= tokens.length)
1042
+ return null;
1043
+ }
1044
+ consumedValueFlag = true;
1045
+ i = shortFlags.length;
1046
+ }
1047
+ break;
1048
+ case "R":
1049
+ case "E":
1050
+ if (isRg && flag === "E") {
1051
+ if (i === shortFlags.length - 1) {
1052
+ ti++;
1053
+ if (ti >= tokens.length)
1054
+ return null;
1055
+ }
1056
+ consumedValueFlag = true;
1057
+ i = shortFlags.length;
1058
+ }
1059
+ break;
1060
+ case "A":
1061
+ case "B":
1062
+ case "C":
1063
+ case "m":
1064
+ if (i === shortFlags.length - 1) {
1065
+ ti++;
1066
+ if (ti >= tokens.length)
1067
+ return null;
1068
+ }
1069
+ i = shortFlags.length;
1070
+ break;
1071
+ case "e": {
1072
+ const inlineValue = shortFlags.slice(i + 1);
1073
+ if (inlineValue) {
1074
+ explicitPatterns.push(inlineValue);
1075
+ } else {
1076
+ ti++;
1077
+ if (ti >= tokens.length)
1078
+ return null;
1079
+ explicitPatterns.push(tokens[ti]);
1080
+ }
1081
+ i = shortFlags.length;
1082
+ break;
1083
+ }
1084
+ default:
1085
+ if (rgValueShorts.has(flag)) {
1086
+ if (i === shortFlags.length - 1) {
1087
+ ti++;
1088
+ if (ti >= tokens.length)
1089
+ return null;
1090
+ }
1091
+ consumedValueFlag = true;
1092
+ i = shortFlags.length;
1093
+ }
1094
+ break;
1095
+ }
1096
+ }
1097
+ void consumedValueFlag;
1098
+ ti++;
1099
+ }
1100
+ const pattern = explicitPatterns.length > 0 ? explicitPatterns[0] : tokens[ti];
1101
+ if (!pattern)
1102
+ return null;
1103
+ let target = explicitPatterns.length > 0 ? tokens[ti] ?? "/" : tokens[ti + 1] ?? "/";
1104
+ if (target === "." || target === "./")
1105
+ target = "/";
1106
+ return {
1107
+ pattern,
1108
+ targetPath: target,
1109
+ ignoreCase,
1110
+ wordMatch,
1111
+ filesOnly,
1112
+ countOnly,
1113
+ lineNumber,
1114
+ invertMatch,
1115
+ fixedString
1116
+ };
1117
+ }
1118
+ async function handleGrepDirect(api, table, sessionsTable, params) {
1119
+ if (!params.pattern)
1120
+ return null;
1121
+ const matchParams = {
1122
+ pattern: params.pattern,
1123
+ ignoreCase: params.ignoreCase,
1124
+ wordMatch: params.wordMatch,
1125
+ filesOnly: params.filesOnly,
1126
+ countOnly: params.countOnly,
1127
+ lineNumber: params.lineNumber,
1128
+ invertMatch: params.invertMatch,
1129
+ fixedString: params.fixedString
1130
+ };
1131
+ const output = await grepBothTables(api, table, sessionsTable, matchParams, params.targetPath);
1132
+ const joined = output.join("\n") || "(no matches)";
1133
+ return capOutputForClaude(joined, { kind: "grep" });
1134
+ }
1135
+
1136
+ // dist/src/hooks/memory-path-utils.js
1137
+ import { homedir as homedir3 } from "node:os";
1138
+ import { join as join4 } from "node:path";
1139
+ var MEMORY_PATH = join4(homedir3(), ".deeplake", "memory");
1140
+ var TILDE_PATH = "~/.deeplake/memory";
1141
+ var HOME_VAR_PATH = "$HOME/.deeplake/memory";
1142
+ function touchesMemory(p) {
1143
+ return p.includes(MEMORY_PATH) || p.includes(TILDE_PATH) || p.includes(HOME_VAR_PATH);
1144
+ }
1145
+ function rewritePaths(cmd) {
1146
+ return cmd.replace(new RegExp(MEMORY_PATH.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") + "/?", "g"), "/").replace(/~\/.deeplake\/memory\/?/g, "/").replace(/\$HOME\/.deeplake\/memory\/?/g, "/").replace(/"\$HOME\/.deeplake\/memory\/?"/g, '"/"');
1147
+ }
1148
+
1149
+ // dist/src/hooks/hermes/pre-tool-use.js
1150
+ var log3 = (msg) => log("hermes-pre-tool-use", msg);
1151
+ async function main() {
1152
+ const input = await readStdin();
1153
+ if (input.tool_name !== "terminal")
1154
+ return;
1155
+ const ti = input.tool_input;
1156
+ const command = ti?.command;
1157
+ if (typeof command !== "string" || command.length === 0)
1158
+ return;
1159
+ if (!touchesMemory(command))
1160
+ return;
1161
+ const rewritten = rewritePaths(command);
1162
+ const grepParams = parseBashGrep(rewritten);
1163
+ if (!grepParams)
1164
+ return;
1165
+ const config = loadConfig();
1166
+ if (!config) {
1167
+ log3("no config \u2014 falling through to Hermes");
1168
+ return;
1169
+ }
1170
+ const api = new DeeplakeApi(config.token, config.apiUrl, config.orgId, config.workspaceId, config.tableName);
1171
+ try {
1172
+ const result = await handleGrepDirect(api, config.tableName, config.sessionsTableName, grepParams);
1173
+ if (result === null)
1174
+ return;
1175
+ log3(`intercepted ${command.slice(0, 80)} \u2192 ${result.length} chars from SQL fast-path`);
1176
+ const message = [
1177
+ result,
1178
+ "",
1179
+ "(Hivemind: blocked the slow grep against ~/.deeplake/memory/ and ran a single SQL query instead. For future recalls, prefer the hivemind_search MCP tool \u2014 same accuracy, no terminal round-trip.)"
1180
+ ].join("\n");
1181
+ process.stdout.write(JSON.stringify({ action: "block", message }));
1182
+ } catch (err) {
1183
+ const msg = err instanceof Error ? err.message : String(err);
1184
+ log3(`fast-path failed, falling through: ${msg}`);
1185
+ }
1186
+ }
1187
+ main().catch((e) => {
1188
+ log3(`fatal: ${e.message}`);
1189
+ process.exit(0);
1190
+ });