@deeplake/hivemind 0.7.31 → 0.7.33

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.
@@ -17,21 +17,21 @@ __export(index_marker_store_exports, {
17
17
  hasFreshIndexMarker: () => hasFreshIndexMarker,
18
18
  writeIndexMarker: () => writeIndexMarker
19
19
  });
20
- import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, writeFileSync } from "node:fs";
21
- import { join as join3 } from "node:path";
20
+ import { existsSync as existsSync2, mkdirSync as mkdirSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "node:fs";
21
+ import { join as join5 } from "node:path";
22
22
  import { tmpdir } from "node:os";
23
23
  function getIndexMarkerDir() {
24
- return process.env.HIVEMIND_INDEX_MARKER_DIR ?? join3(tmpdir(), "hivemind-deeplake-indexes");
24
+ return process.env.HIVEMIND_INDEX_MARKER_DIR ?? join5(tmpdir(), "hivemind-deeplake-indexes");
25
25
  }
26
26
  function buildIndexMarkerPath(workspaceId, orgId, table, suffix) {
27
27
  const markerKey = [workspaceId, orgId, table, suffix].join("__").replace(/[^a-zA-Z0-9_.-]/g, "_");
28
- return join3(getIndexMarkerDir(), `${markerKey}.json`);
28
+ return join5(getIndexMarkerDir(), `${markerKey}.json`);
29
29
  }
30
30
  function hasFreshIndexMarker(markerPath) {
31
31
  if (!existsSync2(markerPath))
32
32
  return false;
33
33
  try {
34
- const raw = JSON.parse(readFileSync2(markerPath, "utf-8"));
34
+ const raw = JSON.parse(readFileSync4(markerPath, "utf-8"));
35
35
  const updatedAt = raw.updatedAt ? new Date(raw.updatedAt).getTime() : NaN;
36
36
  if (!Number.isFinite(updatedAt) || Date.now() - updatedAt > INDEX_MARKER_TTL_MS)
37
37
  return false;
@@ -41,8 +41,8 @@ function hasFreshIndexMarker(markerPath) {
41
41
  }
42
42
  }
43
43
  function writeIndexMarker(markerPath) {
44
- mkdirSync(getIndexMarkerDir(), { recursive: true });
45
- writeFileSync(markerPath, JSON.stringify({ updatedAt: (/* @__PURE__ */ new Date()).toISOString() }), "utf-8");
44
+ mkdirSync3(getIndexMarkerDir(), { recursive: true });
45
+ writeFileSync3(markerPath, JSON.stringify({ updatedAt: (/* @__PURE__ */ new Date()).toISOString() }), "utf-8");
46
46
  }
47
47
  var INDEX_MARKER_TTL_MS;
48
48
  var init_index_marker_store = __esm({
@@ -54,13 +54,13 @@ var init_index_marker_store = __esm({
54
54
 
55
55
  // dist/src/utils/stdin.js
56
56
  function readStdin() {
57
- return new Promise((resolve, reject) => {
57
+ return new Promise((resolve2, reject) => {
58
58
  let data = "";
59
59
  process.stdin.setEncoding("utf-8");
60
60
  process.stdin.on("data", (chunk) => data += chunk);
61
61
  process.stdin.on("end", () => {
62
62
  try {
63
- resolve(JSON.parse(data));
63
+ resolve2(JSON.parse(data));
64
64
  } catch (err) {
65
65
  reject(new Error(`Failed to parse hook input: ${err}`));
66
66
  }
@@ -147,6 +147,125 @@ function deeplakeClientHeader() {
147
147
  return { [DEEPLAKE_CLIENT_HEADER]: deeplakeClientValue() };
148
148
  }
149
149
 
150
+ // dist/src/notifications/queue.js
151
+ import { readFileSync as readFileSync2, writeFileSync, renameSync, mkdirSync, openSync, closeSync, unlinkSync, statSync } from "node:fs";
152
+ import { join as join3, resolve } from "node:path";
153
+ import { homedir as homedir3 } from "node:os";
154
+ import { setTimeout as sleep } from "node:timers/promises";
155
+ var log2 = (msg) => log("notifications-queue", msg);
156
+ var LOCK_RETRY_MAX = 50;
157
+ var LOCK_RETRY_BASE_MS = 5;
158
+ var LOCK_STALE_MS = 5e3;
159
+ function queuePath() {
160
+ return join3(homedir3(), ".deeplake", "notifications-queue.json");
161
+ }
162
+ function lockPath() {
163
+ return `${queuePath()}.lock`;
164
+ }
165
+ function readQueue() {
166
+ try {
167
+ const raw = readFileSync2(queuePath(), "utf-8");
168
+ const parsed = JSON.parse(raw);
169
+ if (!parsed || !Array.isArray(parsed.queue)) {
170
+ log2(`queue malformed \u2192 treating as empty`);
171
+ return { queue: [] };
172
+ }
173
+ return { queue: parsed.queue };
174
+ } catch {
175
+ return { queue: [] };
176
+ }
177
+ }
178
+ function _isQueuePathInsideHome(path, home) {
179
+ const r = resolve(path);
180
+ const h = resolve(home);
181
+ return r.startsWith(h + "/") || r === h;
182
+ }
183
+ function writeQueue(q) {
184
+ const path = queuePath();
185
+ const home = resolve(homedir3());
186
+ if (!_isQueuePathInsideHome(path, home)) {
187
+ throw new Error(`notifications-queue write blocked: ${path} is outside ${home}`);
188
+ }
189
+ mkdirSync(join3(home, ".deeplake"), { recursive: true, mode: 448 });
190
+ const tmp = `${path}.${process.pid}.tmp`;
191
+ writeFileSync(tmp, JSON.stringify(q, null, 2), { mode: 384 });
192
+ renameSync(tmp, path);
193
+ }
194
+ async function withQueueLock(fn) {
195
+ const path = lockPath();
196
+ mkdirSync(join3(homedir3(), ".deeplake"), { recursive: true, mode: 448 });
197
+ let fd = null;
198
+ for (let attempt = 0; attempt < LOCK_RETRY_MAX; attempt++) {
199
+ try {
200
+ fd = openSync(path, "wx", 384);
201
+ break;
202
+ } catch (e) {
203
+ const code = e.code;
204
+ if (code !== "EEXIST")
205
+ throw e;
206
+ try {
207
+ const age = Date.now() - statSync(path).mtimeMs;
208
+ if (age > LOCK_STALE_MS) {
209
+ unlinkSync(path);
210
+ continue;
211
+ }
212
+ } catch {
213
+ }
214
+ const delay = LOCK_RETRY_BASE_MS * (attempt + 1);
215
+ await sleep(delay);
216
+ }
217
+ }
218
+ if (fd === null) {
219
+ log2(`lock acquisition gave up after ${LOCK_RETRY_MAX} attempts \u2014 proceeding unlocked (last-writer-wins)`);
220
+ return fn();
221
+ }
222
+ try {
223
+ return fn();
224
+ } finally {
225
+ try {
226
+ closeSync(fd);
227
+ } catch {
228
+ }
229
+ try {
230
+ unlinkSync(path);
231
+ } catch {
232
+ }
233
+ }
234
+ }
235
+ function sameDedupKey(a, b) {
236
+ if (a.id !== b.id)
237
+ return false;
238
+ return JSON.stringify(a.dedupKey) === JSON.stringify(b.dedupKey);
239
+ }
240
+ async function enqueueNotification(n) {
241
+ await withQueueLock(() => {
242
+ const q = readQueue();
243
+ if (q.queue.some((existing) => sameDedupKey(existing, n))) {
244
+ return;
245
+ }
246
+ q.queue.push(n);
247
+ writeQueue(q);
248
+ });
249
+ }
250
+
251
+ // dist/src/commands/auth-creds.js
252
+ import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, unlinkSync as unlinkSync2 } from "node:fs";
253
+ import { join as join4 } from "node:path";
254
+ import { homedir as homedir4 } from "node:os";
255
+ function configDir() {
256
+ return join4(homedir4(), ".deeplake");
257
+ }
258
+ function credsPath() {
259
+ return join4(configDir(), "credentials.json");
260
+ }
261
+ function loadCredentials() {
262
+ try {
263
+ return JSON.parse(readFileSync3(credsPath(), "utf-8"));
264
+ } catch {
265
+ return null;
266
+ }
267
+ }
268
+
150
269
  // dist/src/deeplake-api.js
151
270
  var indexMarkerStorePromise = null;
152
271
  function getIndexMarkerStore() {
@@ -154,7 +273,7 @@ function getIndexMarkerStore() {
154
273
  indexMarkerStorePromise = Promise.resolve().then(() => (init_index_marker_store(), index_marker_store_exports));
155
274
  return indexMarkerStorePromise;
156
275
  }
157
- var log2 = (msg) => log("sdk", msg);
276
+ var log3 = (msg) => log("sdk", msg);
158
277
  function summarizeSql(sql, maxLen = 220) {
159
278
  const compact = sql.replace(/\s+/g, " ").trim();
160
279
  return compact.length > maxLen ? `${compact.slice(0, maxLen)}...` : compact;
@@ -166,7 +285,38 @@ function traceSql(msg) {
166
285
  process.stderr.write(`[deeplake-sql] ${msg}
167
286
  `);
168
287
  if (process.env.HIVEMIND_DEBUG === "1")
169
- log2(msg);
288
+ log3(msg);
289
+ }
290
+ var _signalledBalanceExhausted = false;
291
+ function maybeSignalBalanceExhausted(status, bodyText) {
292
+ if (status !== 402)
293
+ return;
294
+ if (!bodyText.includes("balance_cents"))
295
+ return;
296
+ if (_signalledBalanceExhausted)
297
+ return;
298
+ _signalledBalanceExhausted = true;
299
+ log3(`balance exhausted \u2014 enqueuing session-start banner (body=${bodyText.slice(0, 120)})`);
300
+ enqueueNotification({
301
+ id: "balance-exhausted",
302
+ severity: "warn",
303
+ transient: true,
304
+ title: "Hivemind credits exhausted \u2014 top up to keep capturing",
305
+ body: `Sessions are not being saved and memory recall is returning empty. Top up at ${billingUrl()} to restore capture and recall.`,
306
+ dedupKey: { reason: "balance-zero" }
307
+ }).catch((e) => {
308
+ log3(`enqueue balance-exhausted failed: ${e instanceof Error ? e.message : String(e)}`);
309
+ });
310
+ }
311
+ function billingUrl() {
312
+ try {
313
+ const c = loadCredentials();
314
+ if (c?.orgName && c?.workspaceId) {
315
+ return `https://deeplake.ai/${encodeURIComponent(c.orgName)}/workspace/${encodeURIComponent(c.workspaceId)}/billing`;
316
+ }
317
+ } catch {
318
+ }
319
+ return "https://deeplake.ai";
170
320
  }
171
321
  var RETRYABLE_CODES = /* @__PURE__ */ new Set([429, 500, 502, 503, 504]);
172
322
  var MAX_RETRIES = 3;
@@ -175,8 +325,8 @@ var MAX_CONCURRENCY = 5;
175
325
  function getQueryTimeoutMs() {
176
326
  return Number(process.env.HIVEMIND_QUERY_TIMEOUT_MS ?? 1e4);
177
327
  }
178
- function sleep(ms) {
179
- return new Promise((resolve) => setTimeout(resolve, ms));
328
+ function sleep2(ms) {
329
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
180
330
  }
181
331
  function isTimeoutError(error) {
182
332
  const name = error instanceof Error ? error.name.toLowerCase() : "";
@@ -206,7 +356,7 @@ var Semaphore = class {
206
356
  this.active++;
207
357
  return;
208
358
  }
209
- await new Promise((resolve) => this.waiting.push(resolve));
359
+ await new Promise((resolve2) => this.waiting.push(resolve2));
210
360
  }
211
361
  release() {
212
362
  this.active--;
@@ -277,8 +427,8 @@ var DeeplakeApi = class {
277
427
  lastError = e instanceof Error ? e : new Error(String(e));
278
428
  if (attempt < MAX_RETRIES) {
279
429
  const delay = BASE_DELAY_MS * Math.pow(2, attempt) + Math.random() * 200;
280
- log2(`query retry ${attempt + 1}/${MAX_RETRIES} (fetch error: ${lastError.message}) in ${delay.toFixed(0)}ms`);
281
- await sleep(delay);
430
+ log3(`query retry ${attempt + 1}/${MAX_RETRIES} (fetch error: ${lastError.message}) in ${delay.toFixed(0)}ms`);
431
+ await sleep2(delay);
282
432
  continue;
283
433
  }
284
434
  throw lastError;
@@ -294,10 +444,11 @@ var DeeplakeApi = class {
294
444
  const alreadyExists = resp.status === 500 && isDuplicateIndexError(text);
295
445
  if (!alreadyExists && attempt < MAX_RETRIES && (RETRYABLE_CODES.has(resp.status) || retryable403)) {
296
446
  const delay = BASE_DELAY_MS * Math.pow(2, attempt) + Math.random() * 200;
297
- log2(`query retry ${attempt + 1}/${MAX_RETRIES} (${resp.status}) in ${delay.toFixed(0)}ms`);
298
- await sleep(delay);
447
+ log3(`query retry ${attempt + 1}/${MAX_RETRIES} (${resp.status}) in ${delay.toFixed(0)}ms`);
448
+ await sleep2(delay);
299
449
  continue;
300
450
  }
451
+ maybeSignalBalanceExhausted(resp.status, text);
301
452
  throw new Error(`Query failed: ${resp.status}: ${text.slice(0, 200)}`);
302
453
  }
303
454
  throw lastError ?? new Error("Query failed: max retries exceeded");
@@ -318,7 +469,7 @@ var DeeplakeApi = class {
318
469
  const chunk = rows.slice(i, i + CONCURRENCY);
319
470
  await Promise.allSettled(chunk.map((r) => this.upsertRowSql(r)));
320
471
  }
321
- log2(`commit: ${rows.length} rows`);
472
+ log3(`commit: ${rows.length} rows`);
322
473
  }
323
474
  async upsertRowSql(row) {
324
475
  const ts = (/* @__PURE__ */ new Date()).toISOString();
@@ -374,7 +525,7 @@ var DeeplakeApi = class {
374
525
  markers.writeIndexMarker(markerPath);
375
526
  return;
376
527
  }
377
- log2(`index "${indexName}" skipped: ${e.message}`);
528
+ log3(`index "${indexName}" skipped: ${e.message}`);
378
529
  }
379
530
  }
380
531
  /**
@@ -464,13 +615,13 @@ var DeeplakeApi = class {
464
615
  };
465
616
  }
466
617
  if (attempt < MAX_RETRIES && RETRYABLE_CODES.has(resp.status)) {
467
- await sleep(BASE_DELAY_MS * Math.pow(2, attempt) + Math.random() * 200);
618
+ await sleep2(BASE_DELAY_MS * Math.pow(2, attempt) + Math.random() * 200);
468
619
  continue;
469
620
  }
470
621
  return { tables: [], cacheable: false };
471
622
  } catch {
472
623
  if (attempt < MAX_RETRIES) {
473
- await sleep(BASE_DELAY_MS * Math.pow(2, attempt));
624
+ await sleep2(BASE_DELAY_MS * Math.pow(2, attempt));
474
625
  continue;
475
626
  }
476
627
  return { tables: [], cacheable: false };
@@ -498,9 +649,9 @@ var DeeplakeApi = class {
498
649
  } catch (err) {
499
650
  lastErr = err;
500
651
  const msg = err instanceof Error ? err.message : String(err);
501
- log2(`CREATE TABLE "${label}" attempt ${attempt + 1}/${OUTER_BACKOFFS_MS.length + 1} failed: ${msg}`);
652
+ log3(`CREATE TABLE "${label}" attempt ${attempt + 1}/${OUTER_BACKOFFS_MS.length + 1} failed: ${msg}`);
502
653
  if (attempt < OUTER_BACKOFFS_MS.length) {
503
- await sleep(OUTER_BACKOFFS_MS[attempt]);
654
+ await sleep2(OUTER_BACKOFFS_MS[attempt]);
504
655
  }
505
656
  }
506
657
  }
@@ -511,9 +662,9 @@ var DeeplakeApi = class {
511
662
  const tbl = sqlIdent(name ?? this.tableName);
512
663
  const tables = await this.listTables();
513
664
  if (!tables.includes(tbl)) {
514
- log2(`table "${tbl}" not found, creating`);
665
+ log3(`table "${tbl}" not found, creating`);
515
666
  await this.createTableWithRetry(`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 '', summary_embedding FLOAT4[], 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 '', plugin_version TEXT NOT NULL DEFAULT '', creation_date TEXT NOT NULL DEFAULT '', last_update_date TEXT NOT NULL DEFAULT '') USING deeplake`, tbl);
516
- log2(`table "${tbl}" created`);
667
+ log3(`table "${tbl}" created`);
517
668
  if (!tables.includes(tbl))
518
669
  this._tablesCache = [...tables, tbl];
519
670
  }
@@ -526,9 +677,9 @@ var DeeplakeApi = class {
526
677
  const safe = sqlIdent(name);
527
678
  const tables = await this.listTables();
528
679
  if (!tables.includes(safe)) {
529
- log2(`table "${safe}" not found, creating`);
680
+ log3(`table "${safe}" not found, creating`);
530
681
  await this.createTableWithRetry(`CREATE TABLE IF NOT EXISTS "${safe}" (id TEXT NOT NULL DEFAULT '', path TEXT NOT NULL DEFAULT '', filename TEXT NOT NULL DEFAULT '', message JSONB, message_embedding FLOAT4[], 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 '', plugin_version TEXT NOT NULL DEFAULT '', creation_date TEXT NOT NULL DEFAULT '', last_update_date TEXT NOT NULL DEFAULT '') USING deeplake`, safe);
531
- log2(`table "${safe}" created`);
682
+ log3(`table "${safe}" created`);
532
683
  if (!tables.includes(safe))
533
684
  this._tablesCache = [...tables, safe];
534
685
  }
@@ -551,9 +702,9 @@ var DeeplakeApi = class {
551
702
  const safe = sqlIdent(name);
552
703
  const tables = await this.listTables();
553
704
  if (!tables.includes(safe)) {
554
- log2(`table "${safe}" not found, creating`);
705
+ log3(`table "${safe}" not found, creating`);
555
706
  await this.createTableWithRetry(`CREATE TABLE IF NOT EXISTS "${safe}" (id TEXT NOT NULL DEFAULT '', name TEXT NOT NULL DEFAULT '', project TEXT NOT NULL DEFAULT '', project_key TEXT NOT NULL DEFAULT '', local_path TEXT NOT NULL DEFAULT '', install TEXT NOT NULL DEFAULT 'project', source_sessions TEXT NOT NULL DEFAULT '[]', source_agent TEXT NOT NULL DEFAULT '', scope TEXT NOT NULL DEFAULT 'me', author TEXT NOT NULL DEFAULT '', description TEXT NOT NULL DEFAULT '', trigger_text TEXT NOT NULL DEFAULT '', body TEXT NOT NULL DEFAULT '', version BIGINT NOT NULL DEFAULT 1, created_at TEXT NOT NULL DEFAULT '', updated_at TEXT NOT NULL DEFAULT '') USING deeplake`, safe);
556
- log2(`table "${safe}" created`);
707
+ log3(`table "${safe}" created`);
557
708
  if (!tables.includes(safe))
558
709
  this._tablesCache = [...tables, safe];
559
710
  }
@@ -570,9 +721,9 @@ function buildSessionPath(config, sessionId) {
570
721
  // dist/src/embeddings/client.js
571
722
  import { connect } from "node:net";
572
723
  import { spawn } from "node:child_process";
573
- import { openSync, closeSync, writeSync, unlinkSync, existsSync as existsSync3, readFileSync as readFileSync3 } from "node:fs";
574
- import { homedir as homedir3 } from "node:os";
575
- import { join as join4 } from "node:path";
724
+ import { openSync as openSync2, closeSync as closeSync2, writeSync, unlinkSync as unlinkSync3, existsSync as existsSync3, readFileSync as readFileSync5 } from "node:fs";
725
+ import { homedir as homedir5 } from "node:os";
726
+ import { join as join6 } from "node:path";
576
727
 
577
728
  // dist/src/embeddings/protocol.js
578
729
  var DEFAULT_SOCKET_DIR = "/tmp";
@@ -586,12 +737,13 @@ function pidPathFor(uid, dir = DEFAULT_SOCKET_DIR) {
586
737
  }
587
738
 
588
739
  // dist/src/embeddings/client.js
589
- var SHARED_DAEMON_PATH = join4(homedir3(), ".hivemind", "embed-deps", "embed-daemon.js");
590
- var log3 = (m) => log("embed-client", m);
740
+ var SHARED_DAEMON_PATH = join6(homedir5(), ".hivemind", "embed-deps", "embed-daemon.js");
741
+ var log4 = (m) => log("embed-client", m);
591
742
  function getUid() {
592
743
  const uid = typeof process.getuid === "function" ? process.getuid() : void 0;
593
744
  return uid !== void 0 ? String(uid) : process.env.USER ?? "default";
594
745
  }
746
+ var _recycledStuckDaemon = false;
595
747
  var EmbedClient = class {
596
748
  socketPath;
597
749
  pidPath;
@@ -600,6 +752,7 @@ var EmbedClient = class {
600
752
  autoSpawn;
601
753
  spawnWaitMs;
602
754
  nextId = 0;
755
+ helloVerified = false;
603
756
  constructor(opts = {}) {
604
757
  const uid = getUid();
605
758
  const dir = opts.socketDir ?? "/tmp";
@@ -616,8 +769,33 @@ var EmbedClient = class {
616
769
  *
617
770
  * Fire-and-forget spawn on miss: if the daemon isn't up, this call returns
618
771
  * null AND kicks off a background spawn. The next call finds a ready daemon.
772
+ *
773
+ * Stuck-daemon recycle: if the daemon returns a transformers-missing
774
+ * error (typical after a marketplace upgrade left an older daemon process
775
+ * alive but with no node_modules accessible from its bundle path), we
776
+ * SIGTERM it and clear its sock/pid so the very next call spawns a fresh
777
+ * daemon from the current bundle. Without this, the stuck daemon would
778
+ * keep poisoning every session until its 10-minute idle-out fires.
619
779
  */
620
780
  async embed(text, kind = "document") {
781
+ const v = await this.embedAttempt(text, kind);
782
+ if (v !== "recycled")
783
+ return v;
784
+ if (!this.autoSpawn)
785
+ return null;
786
+ this.trySpawnDaemon();
787
+ await this.waitForDaemonReady();
788
+ const retry = await this.embedAttempt(text, kind);
789
+ return retry === "recycled" ? null : retry;
790
+ }
791
+ /**
792
+ * One round-trip: connect → verify → embed. Returns:
793
+ * - number[] : embedding vector (happy path)
794
+ * - null : timeout / daemon error / transformers-missing
795
+ * - "recycled": verifyDaemonOnce killed the daemon mid-call;
796
+ * caller should respawn and retry once.
797
+ */
798
+ async embedAttempt(text, kind) {
621
799
  let sock;
622
800
  try {
623
801
  sock = await this.connectOnce();
@@ -627,17 +805,25 @@ var EmbedClient = class {
627
805
  return null;
628
806
  }
629
807
  try {
808
+ const recycled = await this.verifyDaemonOnce(sock);
809
+ if (recycled) {
810
+ return "recycled";
811
+ }
630
812
  const id = String(++this.nextId);
631
813
  const req = { op: "embed", id, kind, text };
632
814
  const resp = await this.sendAndWait(sock, req);
633
815
  if (resp.error || !("embedding" in resp) || !resp.embedding) {
634
- log3(`embed err: ${resp.error ?? "no embedding"}`);
816
+ const err = resp.error ?? "no embedding";
817
+ log4(`embed err: ${err}`);
818
+ if (isTransformersMissingError(err)) {
819
+ this.handleTransformersMissing(err);
820
+ }
635
821
  return null;
636
822
  }
637
823
  return resp.embedding;
638
824
  } catch (e) {
639
825
  const err = e instanceof Error ? e.message : String(e);
640
- log3(`embed failed: ${err}`);
826
+ log4(`embed failed: ${err}`);
641
827
  return null;
642
828
  } finally {
643
829
  try {
@@ -646,6 +832,123 @@ var EmbedClient = class {
646
832
  }
647
833
  }
648
834
  }
835
+ /**
836
+ * Poll for the sock file to come back after `trySpawnDaemon` — used by
837
+ * the recycle retry path. Best-effort: caps at `spawnWaitMs` and
838
+ * returns regardless so the retry attempt can run.
839
+ */
840
+ async waitForDaemonReady() {
841
+ const deadline = Date.now() + this.spawnWaitMs;
842
+ while (Date.now() < deadline) {
843
+ if (existsSync3(this.socketPath))
844
+ return;
845
+ await new Promise((r) => setTimeout(r, 50));
846
+ }
847
+ }
848
+ /**
849
+ * Send a `hello` on first successful connect per EmbedClient instance.
850
+ * If the daemon answers with a path that doesn't match our configured
851
+ * daemonEntry — typical after a marketplace upgrade replaced the bundle
852
+ * — SIGTERM the daemon + clear sock/pid so the next call spawns from the
853
+ * current bundle.
854
+ *
855
+ * `helloVerified` is set ONLY after we've seen a compatible response,
856
+ * so a transient probe failure or a recycle-triggering mismatch leaves
857
+ * the flag false; the next reconnect re-runs verification against
858
+ * whatever daemon is then live (typically the fresh spawn).
859
+ */
860
+ async verifyDaemonOnce(sock) {
861
+ if (this.helloVerified)
862
+ return false;
863
+ if (!this.daemonEntry) {
864
+ this.helloVerified = true;
865
+ return false;
866
+ }
867
+ const id = String(++this.nextId);
868
+ const req = { op: "hello", id };
869
+ let resp;
870
+ try {
871
+ resp = await this.sendAndWait(sock, req);
872
+ } catch (e) {
873
+ log4(`hello probe failed (inconclusive, will retry next connect): ${e instanceof Error ? e.message : String(e)}`);
874
+ return false;
875
+ }
876
+ const hello = resp;
877
+ if (_recycledStuckDaemon) {
878
+ return false;
879
+ }
880
+ if (!hello.daemonPath) {
881
+ _recycledStuckDaemon = true;
882
+ log4(`daemon does not implement hello (older protocol); recycling`);
883
+ this.recycleDaemon(hello.pid);
884
+ return true;
885
+ }
886
+ if (hello.daemonPath !== this.daemonEntry && !existsSync3(hello.daemonPath)) {
887
+ _recycledStuckDaemon = true;
888
+ log4(`daemon path no longer on disk \u2014 running=${hello.daemonPath} (gone) expected=${this.daemonEntry}; recycling`);
889
+ this.recycleDaemon(hello.pid);
890
+ return true;
891
+ }
892
+ this.helloVerified = true;
893
+ return false;
894
+ }
895
+ /**
896
+ * On a transformers-missing error from the daemon, SIGTERM the stuck
897
+ * daemon (the bundle daemon that can't find its deps) and clear
898
+ * sock/pid so the next call spawns fresh.
899
+ *
900
+ * Previously this also enqueued a user-visible "Hivemind embeddings
901
+ * disabled — deps missing" notification telling the user to run
902
+ * `hivemind embeddings install`. The notification was removed because
903
+ * (a) the recycle alone often fixes the issue silently, and (b) the
904
+ * warning kept stacking on top of the primary session-start banner
905
+ * which clashed with the single-slot priority model. The `detail`
906
+ * argument is retained for future telemetry / debug logging.
907
+ */
908
+ handleTransformersMissing(_detail) {
909
+ if (!_recycledStuckDaemon) {
910
+ _recycledStuckDaemon = true;
911
+ this.recycleDaemon(null);
912
+ }
913
+ }
914
+ /**
915
+ * Best-effort SIGTERM + sock/pid cleanup. Tolerant of every missing-file
916
+ * combination and dead-PID cases.
917
+ *
918
+ * Identity check: gate the SIGTERM on the daemon's socket file still
919
+ * existing. We know the daemon was alive moments ago (we either just
920
+ * got a hello response or the caller saw a transformers-missing error
921
+ * the daemon emitted), but if the socket file is gone by the time we
922
+ * try to kill, the daemon process is also gone and the PID we
923
+ * captured may already have been recycled by the OS to an unrelated
924
+ * user process. Mirrors the gate added to `killEmbedDaemon` in the
925
+ * CLI — same failure mode, rarer trigger.
926
+ */
927
+ recycleDaemon(reportedPid) {
928
+ let pid = reportedPid;
929
+ if (pid === null) {
930
+ try {
931
+ pid = Number.parseInt(readFileSync5(this.pidPath, "utf-8").trim(), 10);
932
+ } catch {
933
+ }
934
+ }
935
+ if (Number.isFinite(pid) && pid !== null && pid > 0 && existsSync3(this.socketPath)) {
936
+ try {
937
+ process.kill(pid, "SIGTERM");
938
+ } catch {
939
+ }
940
+ } else if (pid !== null) {
941
+ log4(`recycle: socket gone, skipping SIGTERM on possibly-stale pid ${pid}`);
942
+ }
943
+ try {
944
+ unlinkSync3(this.socketPath);
945
+ } catch {
946
+ }
947
+ try {
948
+ unlinkSync3(this.pidPath);
949
+ } catch {
950
+ }
951
+ }
649
952
  /**
650
953
  * Wait up to spawnWaitMs for the daemon to accept connections, spawning if
651
954
  * necessary. Meant for SessionStart / long-running batches — not the hot path.
@@ -669,7 +972,7 @@ var EmbedClient = class {
669
972
  }
670
973
  }
671
974
  connectOnce() {
672
- return new Promise((resolve, reject) => {
975
+ return new Promise((resolve2, reject) => {
673
976
  const sock = connect(this.socketPath);
674
977
  const to = setTimeout(() => {
675
978
  sock.destroy();
@@ -677,7 +980,7 @@ var EmbedClient = class {
677
980
  }, this.timeoutMs);
678
981
  sock.once("connect", () => {
679
982
  clearTimeout(to);
680
- resolve(sock);
983
+ resolve2(sock);
681
984
  });
682
985
  sock.once("error", (e) => {
683
986
  clearTimeout(to);
@@ -688,16 +991,16 @@ var EmbedClient = class {
688
991
  trySpawnDaemon() {
689
992
  let fd;
690
993
  try {
691
- fd = openSync(this.pidPath, "wx", 384);
994
+ fd = openSync2(this.pidPath, "wx", 384);
692
995
  writeSync(fd, String(process.pid));
693
996
  } catch (e) {
694
997
  if (this.isPidFileStale()) {
695
998
  try {
696
- unlinkSync(this.pidPath);
999
+ unlinkSync3(this.pidPath);
697
1000
  } catch {
698
1001
  }
699
1002
  try {
700
- fd = openSync(this.pidPath, "wx", 384);
1003
+ fd = openSync2(this.pidPath, "wx", 384);
701
1004
  writeSync(fd, String(process.pid));
702
1005
  } catch {
703
1006
  return;
@@ -707,10 +1010,10 @@ var EmbedClient = class {
707
1010
  }
708
1011
  }
709
1012
  if (!this.daemonEntry || !existsSync3(this.daemonEntry)) {
710
- log3(`daemonEntry not configured or missing: ${this.daemonEntry}`);
1013
+ log4(`daemonEntry not configured or missing: ${this.daemonEntry}`);
711
1014
  try {
712
- closeSync(fd);
713
- unlinkSync(this.pidPath);
1015
+ closeSync2(fd);
1016
+ unlinkSync3(this.pidPath);
714
1017
  } catch {
715
1018
  }
716
1019
  return;
@@ -722,14 +1025,14 @@ var EmbedClient = class {
722
1025
  env: process.env
723
1026
  });
724
1027
  child.unref();
725
- log3(`spawned daemon pid=${child.pid}`);
1028
+ log4(`spawned daemon pid=${child.pid}`);
726
1029
  } finally {
727
- closeSync(fd);
1030
+ closeSync2(fd);
728
1031
  }
729
1032
  }
730
1033
  isPidFileStale() {
731
1034
  try {
732
- const raw = readFileSync3(this.pidPath, "utf-8").trim();
1035
+ const raw = readFileSync5(this.pidPath, "utf-8").trim();
733
1036
  const pid = Number(raw);
734
1037
  if (!pid || Number.isNaN(pid))
735
1038
  return true;
@@ -747,7 +1050,7 @@ var EmbedClient = class {
747
1050
  const deadline = Date.now() + this.spawnWaitMs;
748
1051
  let delay = 30;
749
1052
  while (Date.now() < deadline) {
750
- await sleep2(delay);
1053
+ await sleep3(delay);
751
1054
  delay = Math.min(delay * 1.5, 300);
752
1055
  if (!existsSync3(this.socketPath))
753
1056
  continue;
@@ -759,7 +1062,7 @@ var EmbedClient = class {
759
1062
  throw new Error("daemon did not become ready within spawnWaitMs");
760
1063
  }
761
1064
  sendAndWait(sock, req) {
762
- return new Promise((resolve, reject) => {
1065
+ return new Promise((resolve2, reject) => {
763
1066
  let buf = "";
764
1067
  const to = setTimeout(() => {
765
1068
  sock.destroy();
@@ -774,7 +1077,7 @@ var EmbedClient = class {
774
1077
  const line = buf.slice(0, nl);
775
1078
  clearTimeout(to);
776
1079
  try {
777
- resolve(JSON.parse(line));
1080
+ resolve2(JSON.parse(line));
778
1081
  } catch (e) {
779
1082
  reject(e);
780
1083
  }
@@ -791,9 +1094,14 @@ var EmbedClient = class {
791
1094
  });
792
1095
  }
793
1096
  };
794
- function sleep2(ms) {
1097
+ function sleep3(ms) {
795
1098
  return new Promise((r) => setTimeout(r, ms));
796
1099
  }
1100
+ function isTransformersMissingError(err) {
1101
+ if (/hivemind embeddings install/i.test(err))
1102
+ return true;
1103
+ return /@huggingface\/transformers/i.test(err);
1104
+ }
797
1105
 
798
1106
  // dist/src/embeddings/sql.js
799
1107
  function embeddingSqlLiteral(vec) {
@@ -810,23 +1118,105 @@ function embeddingSqlLiteral(vec) {
810
1118
 
811
1119
  // dist/src/embeddings/disable.js
812
1120
  import { createRequire } from "node:module";
813
- import { homedir as homedir4 } from "node:os";
814
- import { join as join5 } from "node:path";
1121
+ import { homedir as homedir7 } from "node:os";
1122
+ import { join as join8 } from "node:path";
815
1123
  import { pathToFileURL } from "node:url";
1124
+
1125
+ // dist/src/user-config.js
1126
+ import { existsSync as existsSync4, mkdirSync as mkdirSync4, readFileSync as readFileSync6, renameSync as renameSync2, writeFileSync as writeFileSync4 } from "node:fs";
1127
+ import { homedir as homedir6 } from "node:os";
1128
+ import { dirname, join as join7 } from "node:path";
1129
+ var _configPath = () => process.env.HIVEMIND_CONFIG_PATH ?? join7(homedir6(), ".deeplake", "config.json");
1130
+ var _cache = null;
1131
+ var _migrated = false;
1132
+ function readUserConfig() {
1133
+ if (_cache !== null)
1134
+ return _cache;
1135
+ const path = _configPath();
1136
+ if (!existsSync4(path)) {
1137
+ _cache = {};
1138
+ return _cache;
1139
+ }
1140
+ try {
1141
+ const raw = readFileSync6(path, "utf-8");
1142
+ const parsed = JSON.parse(raw);
1143
+ _cache = isPlainObject(parsed) ? parsed : {};
1144
+ } catch {
1145
+ _cache = {};
1146
+ }
1147
+ return _cache;
1148
+ }
1149
+ function writeUserConfig(patch) {
1150
+ const current = readUserConfig();
1151
+ const merged = deepMerge(current, patch);
1152
+ const path = _configPath();
1153
+ const dir = dirname(path);
1154
+ if (!existsSync4(dir))
1155
+ mkdirSync4(dir, { recursive: true });
1156
+ const tmp = `${path}.tmp.${process.pid}`;
1157
+ writeFileSync4(tmp, JSON.stringify(merged, null, 2) + "\n", "utf-8");
1158
+ renameSync2(tmp, path);
1159
+ _cache = merged;
1160
+ return merged;
1161
+ }
1162
+ function getEmbeddingsEnabled() {
1163
+ const cfg = readUserConfig();
1164
+ if (cfg.embeddings && typeof cfg.embeddings.enabled === "boolean") {
1165
+ return cfg.embeddings.enabled;
1166
+ }
1167
+ if (_migrated) {
1168
+ return migrationValueFromEnv();
1169
+ }
1170
+ _migrated = true;
1171
+ const enabled = migrationValueFromEnv();
1172
+ try {
1173
+ writeUserConfig({ embeddings: { enabled } });
1174
+ } catch {
1175
+ _cache = { ...cfg ?? {}, embeddings: { ...cfg?.embeddings ?? {}, enabled } };
1176
+ }
1177
+ return enabled;
1178
+ }
1179
+ function migrationValueFromEnv() {
1180
+ const raw = process.env.HIVEMIND_EMBEDDINGS;
1181
+ if (raw === void 0)
1182
+ return false;
1183
+ if (raw === "false")
1184
+ return false;
1185
+ return true;
1186
+ }
1187
+ function isPlainObject(value) {
1188
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1189
+ }
1190
+ function deepMerge(base, patch) {
1191
+ const out = { ...base };
1192
+ for (const key of Object.keys(patch)) {
1193
+ const patchVal = patch[key];
1194
+ const baseVal = base[key];
1195
+ if (isPlainObject(patchVal) && isPlainObject(baseVal)) {
1196
+ out[key] = { ...baseVal, ...patchVal };
1197
+ } else if (patchVal !== void 0) {
1198
+ out[key] = patchVal;
1199
+ }
1200
+ }
1201
+ return out;
1202
+ }
1203
+
1204
+ // dist/src/embeddings/disable.js
816
1205
  var cachedStatus = null;
817
1206
  function defaultResolveTransformers() {
1207
+ const sharedDir = join8(homedir7(), ".hivemind", "embed-deps");
818
1208
  try {
819
- createRequire(import.meta.url).resolve("@huggingface/transformers");
1209
+ createRequire(pathToFileURL(`${sharedDir}/`).href).resolve("@huggingface/transformers");
820
1210
  return;
821
1211
  } catch {
822
1212
  }
823
- const sharedDir = join5(homedir4(), ".hivemind", "embed-deps");
824
- createRequire(pathToFileURL(`${sharedDir}/`).href).resolve("@huggingface/transformers");
1213
+ createRequire(import.meta.url).resolve("@huggingface/transformers");
825
1214
  }
826
1215
  var _resolve = defaultResolveTransformers;
1216
+ var _readEnabled = getEmbeddingsEnabled;
827
1217
  function detectStatus() {
828
- if (process.env.HIVEMIND_EMBEDDINGS === "false")
829
- return "env-disabled";
1218
+ if (!_readEnabled())
1219
+ return "user-disabled";
830
1220
  try {
831
1221
  _resolve();
832
1222
  return "enabled";
@@ -844,55 +1234,120 @@ function embeddingsDisabled() {
844
1234
  return embeddingsStatus() !== "enabled";
845
1235
  }
846
1236
 
1237
+ // dist/src/embeddings/self-heal.js
1238
+ import { existsSync as existsSync5, lstatSync, mkdirSync as mkdirSync5, readlinkSync, renameSync as renameSync3, rmSync, symlinkSync, statSync as statSync2 } from "node:fs";
1239
+ import { homedir as homedir8 } from "node:os";
1240
+ import { basename, dirname as dirname2, join as join9 } from "node:path";
1241
+ function ensurePluginNodeModulesLink(opts) {
1242
+ if (basename(opts.bundleDir) !== "bundle") {
1243
+ return { kind: "not-bundle-layout", bundleDir: opts.bundleDir };
1244
+ }
1245
+ const target = opts.sharedNodeModules ?? join9(homedir8(), ".hivemind", "embed-deps", "node_modules");
1246
+ const pluginDir = dirname2(opts.bundleDir);
1247
+ const link = join9(pluginDir, "node_modules");
1248
+ if (!existsSync5(target)) {
1249
+ return { kind: "shared-deps-missing", target };
1250
+ }
1251
+ let linkStat;
1252
+ try {
1253
+ linkStat = lstatSync(link);
1254
+ } catch {
1255
+ return createSymlinkAtomic(target, link);
1256
+ }
1257
+ if (linkStat.isSymbolicLink()) {
1258
+ let existingTarget;
1259
+ try {
1260
+ existingTarget = readlinkSync(link);
1261
+ } catch (e) {
1262
+ return { kind: "error", detail: `readlink failed: ${e instanceof Error ? e.message : String(e)}` };
1263
+ }
1264
+ if (existingTarget === target) {
1265
+ return { kind: "already-linked", target, link };
1266
+ }
1267
+ try {
1268
+ statSync2(link);
1269
+ return { kind: "linked-elsewhere", link, existingTarget };
1270
+ } catch {
1271
+ try {
1272
+ rmSync(link);
1273
+ } catch {
1274
+ }
1275
+ const recreated = createSymlinkAtomic(target, link);
1276
+ if (recreated.kind === "linked") {
1277
+ return { kind: "stale-link-removed", link, danglingTarget: existingTarget };
1278
+ }
1279
+ return recreated;
1280
+ }
1281
+ }
1282
+ return { kind: "plugin-owns-node-modules", link };
1283
+ }
1284
+ function createSymlinkAtomic(target, link) {
1285
+ try {
1286
+ const parent = dirname2(link);
1287
+ if (!existsSync5(parent))
1288
+ mkdirSync5(parent, { recursive: true });
1289
+ const tmp = `${link}.tmp.${process.pid}`;
1290
+ try {
1291
+ rmSync(tmp, { force: true });
1292
+ } catch {
1293
+ }
1294
+ symlinkSync(target, tmp);
1295
+ renameSync3(tmp, link);
1296
+ return { kind: "linked", target, link };
1297
+ } catch (e) {
1298
+ return { kind: "error", detail: e instanceof Error ? e.message : String(e) };
1299
+ }
1300
+ }
1301
+
847
1302
  // dist/src/hooks/cursor/capture.js
848
1303
  import { fileURLToPath as fileURLToPath3 } from "node:url";
849
- import { dirname as dirname4, join as join15 } from "node:path";
1304
+ import { dirname as dirname6, join as join19 } from "node:path";
850
1305
 
851
1306
  // dist/src/hooks/summary-state.js
852
- import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, writeSync as writeSync2, mkdirSync as mkdirSync2, renameSync, existsSync as existsSync4, unlinkSync as unlinkSync2, openSync as openSync2, closeSync as closeSync2 } from "node:fs";
853
- import { homedir as homedir5 } from "node:os";
854
- import { join as join6 } from "node:path";
1307
+ import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, writeSync as writeSync2, mkdirSync as mkdirSync6, renameSync as renameSync4, existsSync as existsSync6, unlinkSync as unlinkSync4, openSync as openSync3, closeSync as closeSync3 } from "node:fs";
1308
+ import { homedir as homedir9 } from "node:os";
1309
+ import { join as join10 } from "node:path";
855
1310
  var dlog = (msg) => log("summary-state", msg);
856
- var STATE_DIR = join6(homedir5(), ".claude", "hooks", "summary-state");
1311
+ var STATE_DIR = join10(homedir9(), ".claude", "hooks", "summary-state");
857
1312
  var YIELD_BUF = new Int32Array(new SharedArrayBuffer(4));
858
1313
  function statePath(sessionId) {
859
- return join6(STATE_DIR, `${sessionId}.json`);
1314
+ return join10(STATE_DIR, `${sessionId}.json`);
860
1315
  }
861
- function lockPath(sessionId) {
862
- return join6(STATE_DIR, `${sessionId}.lock`);
1316
+ function lockPath2(sessionId) {
1317
+ return join10(STATE_DIR, `${sessionId}.lock`);
863
1318
  }
864
1319
  function readState(sessionId) {
865
1320
  const p = statePath(sessionId);
866
- if (!existsSync4(p))
1321
+ if (!existsSync6(p))
867
1322
  return null;
868
1323
  try {
869
- return JSON.parse(readFileSync4(p, "utf-8"));
1324
+ return JSON.parse(readFileSync7(p, "utf-8"));
870
1325
  } catch {
871
1326
  return null;
872
1327
  }
873
1328
  }
874
1329
  function writeState(sessionId, state) {
875
- mkdirSync2(STATE_DIR, { recursive: true });
1330
+ mkdirSync6(STATE_DIR, { recursive: true });
876
1331
  const p = statePath(sessionId);
877
1332
  const tmp = `${p}.${process.pid}.${Date.now()}.tmp`;
878
- writeFileSync2(tmp, JSON.stringify(state));
879
- renameSync(tmp, p);
1333
+ writeFileSync5(tmp, JSON.stringify(state));
1334
+ renameSync4(tmp, p);
880
1335
  }
881
1336
  function withRmwLock(sessionId, fn) {
882
- mkdirSync2(STATE_DIR, { recursive: true });
1337
+ mkdirSync6(STATE_DIR, { recursive: true });
883
1338
  const rmwLock = statePath(sessionId) + ".rmw";
884
1339
  const deadline = Date.now() + 2e3;
885
1340
  let fd = null;
886
1341
  while (fd === null) {
887
1342
  try {
888
- fd = openSync2(rmwLock, "wx");
1343
+ fd = openSync3(rmwLock, "wx");
889
1344
  } catch (e) {
890
1345
  if (e.code !== "EEXIST")
891
1346
  throw e;
892
1347
  if (Date.now() > deadline) {
893
1348
  dlog(`rmw lock deadline exceeded for ${sessionId}, reclaiming stale lock`);
894
1349
  try {
895
- unlinkSync2(rmwLock);
1350
+ unlinkSync4(rmwLock);
896
1351
  } catch (unlinkErr) {
897
1352
  dlog(`stale rmw lock unlink failed for ${sessionId}: ${unlinkErr.message}`);
898
1353
  }
@@ -904,9 +1359,9 @@ function withRmwLock(sessionId, fn) {
904
1359
  try {
905
1360
  return fn();
906
1361
  } finally {
907
- closeSync2(fd);
1362
+ closeSync3(fd);
908
1363
  try {
909
- unlinkSync2(rmwLock);
1364
+ unlinkSync4(rmwLock);
910
1365
  } catch (unlinkErr) {
911
1366
  dlog(`rmw lock cleanup failed for ${sessionId}: ${unlinkErr.message}`);
912
1367
  }
@@ -941,29 +1396,29 @@ function shouldTrigger(state, cfg, now = Date.now()) {
941
1396
  return false;
942
1397
  }
943
1398
  function tryAcquireLock(sessionId, maxAgeMs = 10 * 60 * 1e3) {
944
- mkdirSync2(STATE_DIR, { recursive: true });
945
- const p = lockPath(sessionId);
946
- if (existsSync4(p)) {
1399
+ mkdirSync6(STATE_DIR, { recursive: true });
1400
+ const p = lockPath2(sessionId);
1401
+ if (existsSync6(p)) {
947
1402
  try {
948
- const ageMs = Date.now() - parseInt(readFileSync4(p, "utf-8"), 10);
1403
+ const ageMs = Date.now() - parseInt(readFileSync7(p, "utf-8"), 10);
949
1404
  if (Number.isFinite(ageMs) && ageMs < maxAgeMs)
950
1405
  return false;
951
1406
  } catch (readErr) {
952
1407
  dlog(`lock file unreadable for ${sessionId}, treating as stale: ${readErr.message}`);
953
1408
  }
954
1409
  try {
955
- unlinkSync2(p);
1410
+ unlinkSync4(p);
956
1411
  } catch (unlinkErr) {
957
1412
  dlog(`could not unlink stale lock for ${sessionId}: ${unlinkErr.message}`);
958
1413
  return false;
959
1414
  }
960
1415
  }
961
1416
  try {
962
- const fd = openSync2(p, "wx");
1417
+ const fd = openSync3(p, "wx");
963
1418
  try {
964
1419
  writeSync2(fd, String(Date.now()));
965
1420
  } finally {
966
- closeSync2(fd);
1421
+ closeSync3(fd);
967
1422
  }
968
1423
  return true;
969
1424
  } catch (e) {
@@ -974,7 +1429,7 @@ function tryAcquireLock(sessionId, maxAgeMs = 10 * 60 * 1e3) {
974
1429
  }
975
1430
  function releaseLock(sessionId) {
976
1431
  try {
977
- unlinkSync2(lockPath(sessionId));
1432
+ unlinkSync4(lockPath2(sessionId));
978
1433
  } catch (e) {
979
1434
  if (e?.code !== "ENOENT") {
980
1435
  dlog(`releaseLock unlink failed for ${sessionId}: ${e.message}`);
@@ -985,20 +1440,20 @@ function releaseLock(sessionId) {
985
1440
  // dist/src/hooks/cursor/spawn-wiki-worker.js
986
1441
  import { spawn as spawn2, execSync } from "node:child_process";
987
1442
  import { fileURLToPath } from "node:url";
988
- import { dirname as dirname2, join as join9 } from "node:path";
989
- import { writeFileSync as writeFileSync3, mkdirSync as mkdirSync4 } from "node:fs";
990
- import { homedir as homedir6, tmpdir as tmpdir2 } from "node:os";
1443
+ import { dirname as dirname4, join as join13 } from "node:path";
1444
+ import { writeFileSync as writeFileSync6, mkdirSync as mkdirSync8 } from "node:fs";
1445
+ import { homedir as homedir10, tmpdir as tmpdir2 } from "node:os";
991
1446
 
992
1447
  // dist/src/utils/wiki-log.js
993
- import { mkdirSync as mkdirSync3, appendFileSync as appendFileSync2 } from "node:fs";
994
- import { join as join7 } from "node:path";
1448
+ import { mkdirSync as mkdirSync7, appendFileSync as appendFileSync2 } from "node:fs";
1449
+ import { join as join11 } from "node:path";
995
1450
  function makeWikiLogger(hooksDir, filename = "deeplake-wiki.log") {
996
- const path = join7(hooksDir, filename);
1451
+ const path = join11(hooksDir, filename);
997
1452
  return {
998
1453
  path,
999
1454
  log(msg) {
1000
1455
  try {
1001
- mkdirSync3(hooksDir, { recursive: true });
1456
+ mkdirSync7(hooksDir, { recursive: true });
1002
1457
  appendFileSync2(path, `[${utcTimestamp()}] ${msg}
1003
1458
  `);
1004
1459
  } catch {
@@ -1008,18 +1463,18 @@ function makeWikiLogger(hooksDir, filename = "deeplake-wiki.log") {
1008
1463
  }
1009
1464
 
1010
1465
  // dist/src/utils/version-check.js
1011
- import { readFileSync as readFileSync5 } from "node:fs";
1012
- import { dirname, join as join8 } from "node:path";
1466
+ import { readFileSync as readFileSync8 } from "node:fs";
1467
+ import { dirname as dirname3, join as join12 } from "node:path";
1013
1468
  function getInstalledVersion(bundleDir, pluginManifestDir) {
1014
1469
  try {
1015
- const pluginJson = join8(bundleDir, "..", pluginManifestDir, "plugin.json");
1016
- const plugin = JSON.parse(readFileSync5(pluginJson, "utf-8"));
1470
+ const pluginJson = join12(bundleDir, "..", pluginManifestDir, "plugin.json");
1471
+ const plugin = JSON.parse(readFileSync8(pluginJson, "utf-8"));
1017
1472
  if (plugin.version)
1018
1473
  return plugin.version;
1019
1474
  } catch {
1020
1475
  }
1021
1476
  try {
1022
- const stamp = readFileSync5(join8(bundleDir, "..", ".hivemind_version"), "utf-8").trim();
1477
+ const stamp = readFileSync8(join12(bundleDir, "..", ".hivemind_version"), "utf-8").trim();
1023
1478
  if (stamp)
1024
1479
  return stamp;
1025
1480
  } catch {
@@ -1034,14 +1489,14 @@ function getInstalledVersion(bundleDir, pluginManifestDir) {
1034
1489
  ]);
1035
1490
  let dir = bundleDir;
1036
1491
  for (let i = 0; i < 5; i++) {
1037
- const candidate = join8(dir, "package.json");
1492
+ const candidate = join12(dir, "package.json");
1038
1493
  try {
1039
- const pkg = JSON.parse(readFileSync5(candidate, "utf-8"));
1494
+ const pkg = JSON.parse(readFileSync8(candidate, "utf-8"));
1040
1495
  if (HIVEMIND_PKG_NAMES.has(pkg.name) && pkg.version)
1041
1496
  return pkg.version;
1042
1497
  } catch {
1043
1498
  }
1044
- const parent = dirname(dir);
1499
+ const parent = dirname3(dir);
1045
1500
  if (parent === dir)
1046
1501
  break;
1047
1502
  dir = parent;
@@ -1050,8 +1505,8 @@ function getInstalledVersion(bundleDir, pluginManifestDir) {
1050
1505
  }
1051
1506
 
1052
1507
  // dist/src/hooks/cursor/spawn-wiki-worker.js
1053
- var HOME = homedir6();
1054
- var wikiLogger = makeWikiLogger(join9(HOME, ".cursor", "hooks"));
1508
+ var HOME = homedir10();
1509
+ var wikiLogger = makeWikiLogger(join13(HOME, ".cursor", "hooks"));
1055
1510
  var WIKI_LOG = wikiLogger.path;
1056
1511
  var WIKI_PROMPT_TEMPLATE = `You are building a personal wiki from a coding session. Your goal is to extract every piece of knowledge \u2014 entities, decisions, relationships, and facts \u2014 into a structured, searchable wiki entry.
1057
1512
 
@@ -1113,11 +1568,11 @@ function findCursorBin() {
1113
1568
  function spawnCursorWikiWorker(opts) {
1114
1569
  const { config, sessionId, cwd, bundleDir, reason } = opts;
1115
1570
  const projectName = cwd.split("/").pop() || "unknown";
1116
- const tmpDir = join9(tmpdir2(), `deeplake-wiki-${sessionId}-${Date.now()}`);
1117
- mkdirSync4(tmpDir, { recursive: true });
1571
+ const tmpDir = join13(tmpdir2(), `deeplake-wiki-${sessionId}-${Date.now()}`);
1572
+ mkdirSync8(tmpDir, { recursive: true });
1118
1573
  const pluginVersion = getInstalledVersion(bundleDir, ".claude-plugin") ?? "";
1119
- const configFile = join9(tmpDir, "config.json");
1120
- writeFileSync3(configFile, JSON.stringify({
1574
+ const configFile = join13(tmpDir, "config.json");
1575
+ writeFileSync6(configFile, JSON.stringify({
1121
1576
  apiUrl: config.apiUrl,
1122
1577
  token: config.token,
1123
1578
  orgId: config.orgId,
@@ -1132,11 +1587,11 @@ function spawnCursorWikiWorker(opts) {
1132
1587
  cursorBin: findCursorBin(),
1133
1588
  cursorModel: process.env.HIVEMIND_CURSOR_MODEL ?? "auto",
1134
1589
  wikiLog: WIKI_LOG,
1135
- hooksDir: join9(HOME, ".cursor", "hooks"),
1590
+ hooksDir: join13(HOME, ".cursor", "hooks"),
1136
1591
  promptTemplate: WIKI_PROMPT_TEMPLATE
1137
1592
  }));
1138
1593
  wikiLog(`${reason}: spawning summary worker for ${sessionId}`);
1139
- const workerPath = join9(bundleDir, "wiki-worker.js");
1594
+ const workerPath = join13(bundleDir, "wiki-worker.js");
1140
1595
  spawn2("nohup", ["node", workerPath, configFile], {
1141
1596
  detached: true,
1142
1597
  stdio: ["ignore", "ignore", "ignore"]
@@ -1144,33 +1599,33 @@ function spawnCursorWikiWorker(opts) {
1144
1599
  wikiLog(`${reason}: spawned summary worker for ${sessionId}`);
1145
1600
  }
1146
1601
  function bundleDirFromImportMeta(importMetaUrl) {
1147
- return dirname2(fileURLToPath(importMetaUrl));
1602
+ return dirname4(fileURLToPath(importMetaUrl));
1148
1603
  }
1149
1604
 
1150
1605
  // dist/src/skillify/spawn-skillify-worker.js
1151
1606
  import { spawn as spawn3 } from "node:child_process";
1152
1607
  import { fileURLToPath as fileURLToPath2 } from "node:url";
1153
- import { dirname as dirname3, join as join11 } from "node:path";
1154
- import { writeFileSync as writeFileSync4, mkdirSync as mkdirSync5, appendFileSync as appendFileSync3, chmodSync } from "node:fs";
1155
- import { homedir as homedir8, tmpdir as tmpdir3 } from "node:os";
1608
+ import { dirname as dirname5, join as join15 } from "node:path";
1609
+ import { writeFileSync as writeFileSync7, mkdirSync as mkdirSync9, appendFileSync as appendFileSync3, chmodSync } from "node:fs";
1610
+ import { homedir as homedir12, tmpdir as tmpdir3 } from "node:os";
1156
1611
 
1157
1612
  // dist/src/skillify/gate-runner.js
1158
- import { existsSync as existsSync5 } from "node:fs";
1613
+ import { existsSync as existsSync7 } from "node:fs";
1159
1614
  import { createRequire as createRequire2 } from "node:module";
1160
- import { homedir as homedir7 } from "node:os";
1161
- import { join as join10 } from "node:path";
1615
+ import { homedir as homedir11 } from "node:os";
1616
+ import { join as join14 } from "node:path";
1162
1617
  var requireForCp = createRequire2(import.meta.url);
1163
1618
  var { execFileSync: runChildProcess } = requireForCp("node:child_process");
1164
1619
  var inheritedEnv = process;
1165
1620
  function firstExistingPath(candidates) {
1166
1621
  for (const c of candidates) {
1167
- if (existsSync5(c))
1622
+ if (existsSync7(c))
1168
1623
  return c;
1169
1624
  }
1170
1625
  return null;
1171
1626
  }
1172
1627
  function findAgentBin(agent) {
1173
- const home = homedir7();
1628
+ const home = homedir11();
1174
1629
  switch (agent) {
1175
1630
  // /usr/bin/<name> is included in every candidate list — that's the
1176
1631
  // common Linux package-manager install path (apt, dnf, pacman). Old
@@ -1179,54 +1634,54 @@ function findAgentBin(agent) {
1179
1634
  // #170 caught the gap.
1180
1635
  case "claude_code":
1181
1636
  return firstExistingPath([
1182
- join10(home, ".claude", "local", "claude"),
1637
+ join14(home, ".claude", "local", "claude"),
1183
1638
  "/usr/local/bin/claude",
1184
1639
  "/usr/bin/claude",
1185
- join10(home, ".npm-global", "bin", "claude"),
1186
- join10(home, ".local", "bin", "claude"),
1640
+ join14(home, ".npm-global", "bin", "claude"),
1641
+ join14(home, ".local", "bin", "claude"),
1187
1642
  "/opt/homebrew/bin/claude"
1188
- ]) ?? join10(home, ".claude", "local", "claude");
1643
+ ]) ?? join14(home, ".claude", "local", "claude");
1189
1644
  case "codex":
1190
1645
  return firstExistingPath([
1191
1646
  "/usr/local/bin/codex",
1192
1647
  "/usr/bin/codex",
1193
- join10(home, ".npm-global", "bin", "codex"),
1194
- join10(home, ".local", "bin", "codex"),
1648
+ join14(home, ".npm-global", "bin", "codex"),
1649
+ join14(home, ".local", "bin", "codex"),
1195
1650
  "/opt/homebrew/bin/codex"
1196
1651
  ]) ?? "/usr/local/bin/codex";
1197
1652
  case "cursor":
1198
1653
  return firstExistingPath([
1199
1654
  "/usr/local/bin/cursor-agent",
1200
1655
  "/usr/bin/cursor-agent",
1201
- join10(home, ".npm-global", "bin", "cursor-agent"),
1202
- join10(home, ".local", "bin", "cursor-agent"),
1656
+ join14(home, ".npm-global", "bin", "cursor-agent"),
1657
+ join14(home, ".local", "bin", "cursor-agent"),
1203
1658
  "/opt/homebrew/bin/cursor-agent"
1204
1659
  ]) ?? "/usr/local/bin/cursor-agent";
1205
1660
  case "hermes":
1206
1661
  return firstExistingPath([
1207
- join10(home, ".local", "bin", "hermes"),
1662
+ join14(home, ".local", "bin", "hermes"),
1208
1663
  "/usr/local/bin/hermes",
1209
1664
  "/usr/bin/hermes",
1210
- join10(home, ".npm-global", "bin", "hermes"),
1665
+ join14(home, ".npm-global", "bin", "hermes"),
1211
1666
  "/opt/homebrew/bin/hermes"
1212
- ]) ?? join10(home, ".local", "bin", "hermes");
1667
+ ]) ?? join14(home, ".local", "bin", "hermes");
1213
1668
  case "pi":
1214
1669
  return firstExistingPath([
1215
- join10(home, ".local", "bin", "pi"),
1670
+ join14(home, ".local", "bin", "pi"),
1216
1671
  "/usr/local/bin/pi",
1217
1672
  "/usr/bin/pi",
1218
- join10(home, ".npm-global", "bin", "pi"),
1673
+ join14(home, ".npm-global", "bin", "pi"),
1219
1674
  "/opt/homebrew/bin/pi"
1220
- ]) ?? join10(home, ".local", "bin", "pi");
1675
+ ]) ?? join14(home, ".local", "bin", "pi");
1221
1676
  }
1222
1677
  }
1223
1678
 
1224
1679
  // dist/src/skillify/spawn-skillify-worker.js
1225
- var HOME2 = homedir8();
1226
- var SKILLIFY_LOG = join11(HOME2, ".claude", "hooks", "skillify.log");
1680
+ var HOME2 = homedir12();
1681
+ var SKILLIFY_LOG = join15(HOME2, ".claude", "hooks", "skillify.log");
1227
1682
  function skillifyLog(msg) {
1228
1683
  try {
1229
- mkdirSync5(dirname3(SKILLIFY_LOG), { recursive: true });
1684
+ mkdirSync9(dirname5(SKILLIFY_LOG), { recursive: true });
1230
1685
  appendFileSync3(SKILLIFY_LOG, `[${utcTimestamp()}] ${msg}
1231
1686
  `);
1232
1687
  } catch {
@@ -1234,11 +1689,11 @@ function skillifyLog(msg) {
1234
1689
  }
1235
1690
  function spawnSkillifyWorker(opts) {
1236
1691
  const { config, cwd, projectKey, project, bundleDir, agent, scopeConfig, currentSessionId, reason } = opts;
1237
- const tmpDir = join11(tmpdir3(), `deeplake-skillify-${projectKey}-${Date.now()}`);
1238
- mkdirSync5(tmpDir, { recursive: true, mode: 448 });
1692
+ const tmpDir = join15(tmpdir3(), `deeplake-skillify-${projectKey}-${Date.now()}`);
1693
+ mkdirSync9(tmpDir, { recursive: true, mode: 448 });
1239
1694
  const gateBin = findAgentBin(agent);
1240
- const configFile = join11(tmpDir, "config.json");
1241
- writeFileSync4(configFile, JSON.stringify({
1695
+ const configFile = join15(tmpDir, "config.json");
1696
+ writeFileSync7(configFile, JSON.stringify({
1242
1697
  apiUrl: config.apiUrl,
1243
1698
  token: config.token,
1244
1699
  orgId: config.orgId,
@@ -1268,7 +1723,7 @@ function spawnSkillifyWorker(opts) {
1268
1723
  } catch {
1269
1724
  }
1270
1725
  skillifyLog(`${reason}: spawning skillify worker for project=${project} key=${projectKey}`);
1271
- const workerPath = join11(bundleDir, "skillify-worker.js");
1726
+ const workerPath = join15(bundleDir, "skillify-worker.js");
1272
1727
  spawn3("nohup", ["node", workerPath, configFile], {
1273
1728
  detached: true,
1274
1729
  stdio: ["ignore", "ignore", "ignore"]
@@ -1277,31 +1732,31 @@ function spawnSkillifyWorker(opts) {
1277
1732
  }
1278
1733
 
1279
1734
  // dist/src/skillify/state.js
1280
- import { readFileSync as readFileSync6, writeFileSync as writeFileSync5, writeSync as writeSync3, mkdirSync as mkdirSync6, renameSync as renameSync3, existsSync as existsSync7, unlinkSync as unlinkSync3, openSync as openSync3, closeSync as closeSync3 } from "node:fs";
1735
+ import { readFileSync as readFileSync9, writeFileSync as writeFileSync8, writeSync as writeSync3, mkdirSync as mkdirSync10, renameSync as renameSync6, existsSync as existsSync9, unlinkSync as unlinkSync5, openSync as openSync4, closeSync as closeSync4 } from "node:fs";
1281
1736
  import { execSync as execSync2 } from "node:child_process";
1282
- import { homedir as homedir10 } from "node:os";
1737
+ import { homedir as homedir14 } from "node:os";
1283
1738
  import { createHash } from "node:crypto";
1284
- import { join as join13, basename } from "node:path";
1739
+ import { join as join17, basename as basename2 } from "node:path";
1285
1740
 
1286
1741
  // dist/src/skillify/legacy-migration.js
1287
- import { existsSync as existsSync6, renameSync as renameSync2 } from "node:fs";
1288
- import { homedir as homedir9 } from "node:os";
1289
- import { join as join12 } from "node:path";
1742
+ import { existsSync as existsSync8, renameSync as renameSync5 } from "node:fs";
1743
+ import { homedir as homedir13 } from "node:os";
1744
+ import { join as join16 } from "node:path";
1290
1745
  var dlog2 = (msg) => log("skillify-migrate", msg);
1291
1746
  var attempted = false;
1292
1747
  function migrateLegacyStateDir() {
1293
1748
  if (attempted)
1294
1749
  return;
1295
1750
  attempted = true;
1296
- const root = join12(homedir9(), ".deeplake", "state");
1297
- const legacy = join12(root, "skilify");
1298
- const current = join12(root, "skillify");
1299
- if (!existsSync6(legacy))
1751
+ const root = join16(homedir13(), ".deeplake", "state");
1752
+ const legacy = join16(root, "skilify");
1753
+ const current = join16(root, "skillify");
1754
+ if (!existsSync8(legacy))
1300
1755
  return;
1301
- if (existsSync6(current))
1756
+ if (existsSync8(current))
1302
1757
  return;
1303
1758
  try {
1304
- renameSync2(legacy, current);
1759
+ renameSync5(legacy, current);
1305
1760
  dlog2(`migrated ${legacy} -> ${current}`);
1306
1761
  } catch (err) {
1307
1762
  const code = err.code;
@@ -1315,17 +1770,17 @@ function migrateLegacyStateDir() {
1315
1770
 
1316
1771
  // dist/src/skillify/state.js
1317
1772
  var dlog3 = (msg) => log("skillify-state", msg);
1318
- var STATE_DIR2 = join13(homedir10(), ".deeplake", "state", "skillify");
1773
+ var STATE_DIR2 = join17(homedir14(), ".deeplake", "state", "skillify");
1319
1774
  var YIELD_BUF2 = new Int32Array(new SharedArrayBuffer(4));
1320
1775
  var TRIGGER_THRESHOLD = (() => {
1321
1776
  const n = Number(process.env.HIVEMIND_SKILLIFY_EVERY_N_TURNS ?? "");
1322
1777
  return Number.isInteger(n) && n > 0 ? n : 20;
1323
1778
  })();
1324
1779
  function statePath2(projectKey) {
1325
- return join13(STATE_DIR2, `${projectKey}.json`);
1780
+ return join17(STATE_DIR2, `${projectKey}.json`);
1326
1781
  }
1327
- function lockPath2(projectKey) {
1328
- return join13(STATE_DIR2, `${projectKey}.lock`);
1782
+ function lockPath3(projectKey) {
1783
+ return join17(STATE_DIR2, `${projectKey}.lock`);
1329
1784
  }
1330
1785
  var DEFAULT_PORTS = {
1331
1786
  http: "80",
@@ -1353,7 +1808,7 @@ function normalizeGitRemoteUrl(url) {
1353
1808
  return s.toLowerCase();
1354
1809
  }
1355
1810
  function deriveProjectKey(cwd) {
1356
- const project = basename(cwd) || "unknown";
1811
+ const project = basename2(cwd) || "unknown";
1357
1812
  let signature = null;
1358
1813
  try {
1359
1814
  const raw = execSync2("git config --get remote.origin.url", {
@@ -1371,38 +1826,38 @@ function deriveProjectKey(cwd) {
1371
1826
  function readState2(projectKey) {
1372
1827
  migrateLegacyStateDir();
1373
1828
  const p = statePath2(projectKey);
1374
- if (!existsSync7(p))
1829
+ if (!existsSync9(p))
1375
1830
  return null;
1376
1831
  try {
1377
- return JSON.parse(readFileSync6(p, "utf-8"));
1832
+ return JSON.parse(readFileSync9(p, "utf-8"));
1378
1833
  } catch {
1379
1834
  return null;
1380
1835
  }
1381
1836
  }
1382
1837
  function writeState2(projectKey, state) {
1383
1838
  migrateLegacyStateDir();
1384
- mkdirSync6(STATE_DIR2, { recursive: true });
1839
+ mkdirSync10(STATE_DIR2, { recursive: true });
1385
1840
  const p = statePath2(projectKey);
1386
1841
  const tmp = `${p}.${process.pid}.${Date.now()}.tmp`;
1387
- writeFileSync5(tmp, JSON.stringify(state, null, 2));
1388
- renameSync3(tmp, p);
1842
+ writeFileSync8(tmp, JSON.stringify(state, null, 2));
1843
+ renameSync6(tmp, p);
1389
1844
  }
1390
1845
  function withRmwLock2(projectKey, fn) {
1391
1846
  migrateLegacyStateDir();
1392
- mkdirSync6(STATE_DIR2, { recursive: true });
1393
- const rmw = lockPath2(projectKey) + ".rmw";
1847
+ mkdirSync10(STATE_DIR2, { recursive: true });
1848
+ const rmw = lockPath3(projectKey) + ".rmw";
1394
1849
  const deadline = Date.now() + 2e3;
1395
1850
  let fd = null;
1396
1851
  while (fd === null) {
1397
1852
  try {
1398
- fd = openSync3(rmw, "wx");
1853
+ fd = openSync4(rmw, "wx");
1399
1854
  } catch (e) {
1400
1855
  if (e.code !== "EEXIST")
1401
1856
  throw e;
1402
1857
  if (Date.now() > deadline) {
1403
1858
  dlog3(`rmw lock deadline exceeded for ${projectKey}, reclaiming stale lock`);
1404
1859
  try {
1405
- unlinkSync3(rmw);
1860
+ unlinkSync5(rmw);
1406
1861
  } catch (unlinkErr) {
1407
1862
  dlog3(`stale rmw lock unlink failed for ${projectKey}: ${unlinkErr.message}`);
1408
1863
  }
@@ -1414,9 +1869,9 @@ function withRmwLock2(projectKey, fn) {
1414
1869
  try {
1415
1870
  return fn();
1416
1871
  } finally {
1417
- closeSync3(fd);
1872
+ closeSync4(fd);
1418
1873
  try {
1419
- unlinkSync3(rmw);
1874
+ unlinkSync5(rmw);
1420
1875
  } catch (unlinkErr) {
1421
1876
  dlog3(`rmw lock cleanup failed for ${projectKey}: ${unlinkErr.message}`);
1422
1877
  }
@@ -1449,29 +1904,29 @@ function resetCounter(projectKey) {
1449
1904
  }
1450
1905
  function tryAcquireWorkerLock(projectKey, maxAgeMs = 10 * 60 * 1e3) {
1451
1906
  migrateLegacyStateDir();
1452
- mkdirSync6(STATE_DIR2, { recursive: true });
1453
- const p = lockPath2(projectKey);
1454
- if (existsSync7(p)) {
1907
+ mkdirSync10(STATE_DIR2, { recursive: true });
1908
+ const p = lockPath3(projectKey);
1909
+ if (existsSync9(p)) {
1455
1910
  try {
1456
- const ageMs = Date.now() - parseInt(readFileSync6(p, "utf-8"), 10);
1911
+ const ageMs = Date.now() - parseInt(readFileSync9(p, "utf-8"), 10);
1457
1912
  if (Number.isFinite(ageMs) && ageMs < maxAgeMs)
1458
1913
  return false;
1459
1914
  } catch (readErr) {
1460
1915
  dlog3(`worker lock unreadable for ${projectKey}, treating as stale: ${readErr.message}`);
1461
1916
  }
1462
1917
  try {
1463
- unlinkSync3(p);
1918
+ unlinkSync5(p);
1464
1919
  } catch (unlinkErr) {
1465
1920
  dlog3(`could not unlink stale worker lock for ${projectKey}: ${unlinkErr.message}`);
1466
1921
  return false;
1467
1922
  }
1468
1923
  }
1469
1924
  try {
1470
- const fd = openSync3(p, "wx");
1925
+ const fd = openSync4(p, "wx");
1471
1926
  try {
1472
1927
  writeSync3(fd, String(Date.now()));
1473
1928
  } finally {
1474
- closeSync3(fd);
1929
+ closeSync4(fd);
1475
1930
  }
1476
1931
  return true;
1477
1932
  } catch {
@@ -1479,26 +1934,26 @@ function tryAcquireWorkerLock(projectKey, maxAgeMs = 10 * 60 * 1e3) {
1479
1934
  }
1480
1935
  }
1481
1936
  function releaseWorkerLock(projectKey) {
1482
- const p = lockPath2(projectKey);
1937
+ const p = lockPath3(projectKey);
1483
1938
  try {
1484
- unlinkSync3(p);
1939
+ unlinkSync5(p);
1485
1940
  } catch {
1486
1941
  }
1487
1942
  }
1488
1943
 
1489
1944
  // dist/src/skillify/scope-config.js
1490
- import { existsSync as existsSync8, mkdirSync as mkdirSync7, readFileSync as readFileSync7, writeFileSync as writeFileSync6 } from "node:fs";
1491
- import { homedir as homedir11 } from "node:os";
1492
- import { join as join14 } from "node:path";
1493
- var STATE_DIR3 = join14(homedir11(), ".deeplake", "state", "skillify");
1494
- var CONFIG_PATH = join14(STATE_DIR3, "config.json");
1945
+ import { existsSync as existsSync10, mkdirSync as mkdirSync11, readFileSync as readFileSync10, writeFileSync as writeFileSync9 } from "node:fs";
1946
+ import { homedir as homedir15 } from "node:os";
1947
+ import { join as join18 } from "node:path";
1948
+ var STATE_DIR3 = join18(homedir15(), ".deeplake", "state", "skillify");
1949
+ var CONFIG_PATH = join18(STATE_DIR3, "config.json");
1495
1950
  var DEFAULT = { scope: "me", team: [], install: "project" };
1496
1951
  function loadScopeConfig() {
1497
1952
  migrateLegacyStateDir();
1498
- if (!existsSync8(CONFIG_PATH))
1953
+ if (!existsSync10(CONFIG_PATH))
1499
1954
  return DEFAULT;
1500
1955
  try {
1501
- const raw = JSON.parse(readFileSync7(CONFIG_PATH, "utf-8"));
1956
+ const raw = JSON.parse(readFileSync10(CONFIG_PATH, "utf-8"));
1502
1957
  const scope = raw.scope === "team" ? "team" : raw.scope === "org" ? "team" : "me";
1503
1958
  const team = Array.isArray(raw.team) ? raw.team.filter((s) => typeof s === "string") : [];
1504
1959
  const install = raw.install === "global" ? "global" : "project";
@@ -1549,12 +2004,18 @@ function tryStopCounterTrigger(opts) {
1549
2004
  }
1550
2005
 
1551
2006
  // dist/src/hooks/cursor/capture.js
1552
- var log4 = (msg) => log("cursor-capture", msg);
2007
+ var log5 = (msg) => log("cursor-capture", msg);
1553
2008
  function resolveEmbedDaemonPath() {
1554
- return join15(dirname4(fileURLToPath3(import.meta.url)), "embeddings", "embed-daemon.js");
2009
+ return join19(dirname6(fileURLToPath3(import.meta.url)), "embeddings", "embed-daemon.js");
1555
2010
  }
1556
- var __bundleDir = dirname4(fileURLToPath3(import.meta.url));
2011
+ var __bundleDir = dirname6(fileURLToPath3(import.meta.url));
1557
2012
  var PLUGIN_VERSION = getInstalledVersion(__bundleDir, ".claude-plugin") ?? "";
2013
+ if (!embeddingsDisabled()) {
2014
+ try {
2015
+ ensurePluginNodeModulesLink({ bundleDir: __bundleDir });
2016
+ } catch {
2017
+ }
2018
+ }
1558
2019
  var CAPTURE = process.env.HIVEMIND_CAPTURE !== "false";
1559
2020
  function resolveCwd(input) {
1560
2021
  if (typeof input.cwd === "string" && input.cwd)
@@ -1570,7 +2031,7 @@ async function main() {
1570
2031
  const input = await readStdin();
1571
2032
  const config = loadConfig();
1572
2033
  if (!config) {
1573
- log4("no config");
2034
+ log5("no config");
1574
2035
  return;
1575
2036
  }
1576
2037
  const sessionId = input.conversation_id ?? `cursor-${Date.now()}`;
@@ -1589,10 +2050,10 @@ async function main() {
1589
2050
  };
1590
2051
  let entry = null;
1591
2052
  if (event === "beforeSubmitPrompt" && typeof input.prompt === "string") {
1592
- log4(`user session=${sessionId}`);
2053
+ log5(`user session=${sessionId}`);
1593
2054
  entry = { id: crypto.randomUUID(), ...meta, type: "user_message", content: input.prompt };
1594
2055
  } else if (event === "postToolUse" && typeof input.tool_name === "string") {
1595
- log4(`tool=${input.tool_name} session=${sessionId}`);
2056
+ log5(`tool=${input.tool_name} session=${sessionId}`);
1596
2057
  entry = {
1597
2058
  id: crypto.randomUUID(),
1598
2059
  ...meta,
@@ -1604,10 +2065,10 @@ async function main() {
1604
2065
  tool_response: typeof input.tool_output === "string" ? input.tool_output : JSON.stringify(input.tool_output)
1605
2066
  };
1606
2067
  } else if (event === "afterAgentResponse" && typeof input.text === "string") {
1607
- log4(`assistant session=${sessionId}`);
2068
+ log5(`assistant session=${sessionId}`);
1608
2069
  entry = { id: crypto.randomUUID(), ...meta, type: "assistant_message", content: input.text };
1609
2070
  } else if (event === "stop") {
1610
- log4(`stop session=${sessionId} status=${input.status ?? "unknown"}`);
2071
+ log5(`stop session=${sessionId} status=${input.status ?? "unknown"}`);
1611
2072
  entry = {
1612
2073
  id: crypto.randomUUID(),
1613
2074
  ...meta,
@@ -1616,12 +2077,12 @@ async function main() {
1616
2077
  loop_count: input.loop_count
1617
2078
  };
1618
2079
  } else {
1619
- log4(`unknown event: ${event}, skipping`);
2080
+ log5(`unknown event: ${event}, skipping`);
1620
2081
  return;
1621
2082
  }
1622
2083
  const sessionPath = buildSessionPath(config, sessionId);
1623
2084
  const line = JSON.stringify(entry);
1624
- log4(`writing to ${sessionPath}`);
2085
+ log5(`writing to ${sessionPath}`);
1625
2086
  const projectName = cwd.split("/").pop() || "unknown";
1626
2087
  const filename = sessionPath.split("/").pop() ?? "";
1627
2088
  const jsonForSql = line.replace(/'/g, "''");
@@ -1632,14 +2093,14 @@ async function main() {
1632
2093
  await api.query(insertSql);
1633
2094
  } catch (e) {
1634
2095
  if (e.message?.includes("permission denied") || e.message?.includes("does not exist")) {
1635
- log4("table missing, creating and retrying");
2096
+ log5("table missing, creating and retrying");
1636
2097
  await api.ensureSessionsTable(sessionsTable);
1637
2098
  await api.query(insertSql);
1638
2099
  } else {
1639
2100
  throw e;
1640
2101
  }
1641
2102
  }
1642
- log4("capture ok \u2192 cloud");
2103
+ log5("capture ok \u2192 cloud");
1643
2104
  maybeTriggerPeriodicSummary(sessionId, cwd, config);
1644
2105
  if (event === "afterAgentResponse" && process.env.HIVEMIND_WIKI_WORKER !== "1" && process.env.HIVEMIND_SKILLIFY_WORKER !== "1") {
1645
2106
  tryStopCounterTrigger({
@@ -1660,7 +2121,7 @@ function maybeTriggerPeriodicSummary(sessionId, cwd, config) {
1660
2121
  if (!shouldTrigger(state, cfg))
1661
2122
  return;
1662
2123
  if (!tryAcquireLock(sessionId)) {
1663
- log4(`periodic trigger suppressed (lock held) session=${sessionId}`);
2124
+ log5(`periodic trigger suppressed (lock held) session=${sessionId}`);
1664
2125
  return;
1665
2126
  }
1666
2127
  wikiLog(`Periodic: threshold hit (total=${state.totalCount}, since=${state.totalCount - state.lastSummaryCount}, N=${cfg.everyNMessages}, hours=${cfg.everyHours})`);
@@ -1673,17 +2134,17 @@ function maybeTriggerPeriodicSummary(sessionId, cwd, config) {
1673
2134
  reason: "Periodic"
1674
2135
  });
1675
2136
  } catch (e) {
1676
- log4(`periodic spawn failed: ${e.message}`);
2137
+ log5(`periodic spawn failed: ${e.message}`);
1677
2138
  try {
1678
2139
  releaseLock(sessionId);
1679
2140
  } catch {
1680
2141
  }
1681
2142
  }
1682
2143
  } catch (e) {
1683
- log4(`periodic trigger error: ${e.message}`);
2144
+ log5(`periodic trigger error: ${e.message}`);
1684
2145
  }
1685
2146
  }
1686
2147
  main().catch((e) => {
1687
- log4(`fatal: ${e.message}`);
2148
+ log5(`fatal: ${e.message}`);
1688
2149
  process.exit(0);
1689
2150
  });