@deeplake/hivemind 0.6.48 → 0.7.9

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.
Files changed (45) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/README.md +244 -20
  4. package/bundle/cli.js +1369 -112
  5. package/codex/bundle/capture.js +546 -96
  6. package/codex/bundle/commands/auth-login.js +290 -81
  7. package/codex/bundle/embeddings/embed-daemon.js +243 -0
  8. package/codex/bundle/pre-tool-use.js +666 -111
  9. package/codex/bundle/session-start-setup.js +231 -64
  10. package/codex/bundle/session-start.js +52 -13
  11. package/codex/bundle/shell/deeplake-shell.js +716 -119
  12. package/codex/bundle/skilify-worker.js +907 -0
  13. package/codex/bundle/stop.js +819 -79
  14. package/codex/bundle/wiki-worker.js +312 -11
  15. package/cursor/bundle/capture.js +1116 -64
  16. package/cursor/bundle/commands/auth-login.js +290 -81
  17. package/cursor/bundle/embeddings/embed-daemon.js +243 -0
  18. package/cursor/bundle/pre-tool-use.js +598 -77
  19. package/cursor/bundle/session-end.js +520 -2
  20. package/cursor/bundle/session-start.js +257 -65
  21. package/cursor/bundle/shell/deeplake-shell.js +716 -119
  22. package/cursor/bundle/skilify-worker.js +907 -0
  23. package/cursor/bundle/wiki-worker.js +571 -0
  24. package/hermes/bundle/capture.js +1119 -65
  25. package/hermes/bundle/commands/auth-login.js +290 -81
  26. package/hermes/bundle/embeddings/embed-daemon.js +243 -0
  27. package/hermes/bundle/pre-tool-use.js +597 -76
  28. package/hermes/bundle/session-end.js +522 -1
  29. package/hermes/bundle/session-start.js +260 -65
  30. package/hermes/bundle/shell/deeplake-shell.js +716 -119
  31. package/hermes/bundle/skilify-worker.js +907 -0
  32. package/hermes/bundle/wiki-worker.js +572 -0
  33. package/mcp/bundle/server.js +290 -75
  34. package/openclaw/dist/chunks/auth-creds-AEKS6D3P.js +14 -0
  35. package/openclaw/dist/chunks/chunk-SRCBBT4H.js +37 -0
  36. package/openclaw/dist/chunks/config-ZLH6JFJS.js +34 -0
  37. package/openclaw/dist/chunks/index-marker-store-PGT5CW6T.js +33 -0
  38. package/openclaw/dist/chunks/setup-config-C35UK4LP.js +114 -0
  39. package/openclaw/dist/index.js +929 -710
  40. package/openclaw/dist/skilify-worker.js +907 -0
  41. package/openclaw/openclaw.plugin.json +1 -1
  42. package/openclaw/package.json +1 -1
  43. package/openclaw/skills/SKILL.md +19 -0
  44. package/package.json +7 -1
  45. package/pi/extension-source/hivemind.ts +603 -22
package/bundle/cli.js CHANGED
@@ -1,4 +1,56 @@
1
1
  #!/usr/bin/env node
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __esm = (fn, res) => function __init() {
5
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
6
+ };
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+
12
+ // dist/src/index-marker-store.js
13
+ var index_marker_store_exports = {};
14
+ __export(index_marker_store_exports, {
15
+ buildIndexMarkerPath: () => buildIndexMarkerPath,
16
+ getIndexMarkerDir: () => getIndexMarkerDir,
17
+ hasFreshIndexMarker: () => hasFreshIndexMarker,
18
+ writeIndexMarker: () => writeIndexMarker
19
+ });
20
+ import { existsSync as existsSync11, mkdirSync as mkdirSync3, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "node:fs";
21
+ import { join as join14 } from "node:path";
22
+ import { tmpdir } from "node:os";
23
+ function getIndexMarkerDir() {
24
+ return process.env.HIVEMIND_INDEX_MARKER_DIR ?? join14(tmpdir(), "hivemind-deeplake-indexes");
25
+ }
26
+ function buildIndexMarkerPath(workspaceId, orgId, table, suffix) {
27
+ const markerKey = [workspaceId, orgId, table, suffix].join("__").replace(/[^a-zA-Z0-9_.-]/g, "_");
28
+ return join14(getIndexMarkerDir(), `${markerKey}.json`);
29
+ }
30
+ function hasFreshIndexMarker(markerPath) {
31
+ if (!existsSync11(markerPath))
32
+ return false;
33
+ try {
34
+ const raw = JSON.parse(readFileSync8(markerPath, "utf-8"));
35
+ const updatedAt = raw.updatedAt ? new Date(raw.updatedAt).getTime() : NaN;
36
+ if (!Number.isFinite(updatedAt) || Date.now() - updatedAt > INDEX_MARKER_TTL_MS)
37
+ return false;
38
+ return true;
39
+ } catch {
40
+ return false;
41
+ }
42
+ }
43
+ function writeIndexMarker(markerPath) {
44
+ mkdirSync3(getIndexMarkerDir(), { recursive: true });
45
+ writeFileSync5(markerPath, JSON.stringify({ updatedAt: (/* @__PURE__ */ new Date()).toISOString() }), "utf-8");
46
+ }
47
+ var INDEX_MARKER_TTL_MS;
48
+ var init_index_marker_store = __esm({
49
+ "dist/src/index-marker-store.js"() {
50
+ "use strict";
51
+ INDEX_MARKER_TTL_MS = Number(process.env.HIVEMIND_INDEX_MARKER_TTL_MS ?? 6 * 60 * 6e4);
52
+ }
53
+ });
2
54
 
3
55
  // dist/src/cli/install-claude.js
4
56
  import { execFileSync } from "node:child_process";
@@ -10,6 +62,19 @@ import { homedir } from "node:os";
10
62
  import { fileURLToPath } from "node:url";
11
63
  var HOME = homedir();
12
64
  function pkgRoot() {
65
+ let dir = fileURLToPath(new URL(".", import.meta.url));
66
+ for (let i = 0; i < 8; i++) {
67
+ try {
68
+ const pkg = JSON.parse(readFileSync(join(dir, "package.json"), "utf-8"));
69
+ if (pkg.name === "@deeplake/hivemind" || pkg.name === "hivemind")
70
+ return dir;
71
+ } catch {
72
+ }
73
+ const parent = dirname(dir);
74
+ if (parent === dir)
75
+ break;
76
+ dir = parent;
77
+ }
13
78
  return fileURLToPath(new URL("..", import.meta.url));
14
79
  }
15
80
  function ensureDir(path, mode = 493) {
@@ -112,6 +177,7 @@ function pluginAlreadyInstalled() {
112
177
  return false;
113
178
  return r.stdout.includes(PLUGIN_KEY);
114
179
  }
180
+ var PLUGIN_SCOPES = ["user", "project", "local", "managed"];
115
181
  function installClaude() {
116
182
  requireClaudeCli();
117
183
  if (!marketplaceAlreadyAdded()) {
@@ -125,9 +191,15 @@ function installClaude() {
125
191
  if (!inst.ok) {
126
192
  throw new Error(`Failed to install hivemind plugin: ${inst.stderr.slice(0, 200)}`);
127
193
  }
194
+ log(` Claude Code installed via marketplace ${MARKETPLACE_SOURCE}`);
195
+ } else {
196
+ runClaude(["plugin", "marketplace", "update", MARKETPLACE_NAME]);
197
+ for (const scope of PLUGIN_SCOPES) {
198
+ runClaude(["plugin", "update", PLUGIN_KEY, "--scope", scope]);
199
+ }
200
+ log(` Claude Code refreshed via marketplace ${MARKETPLACE_SOURCE}`);
128
201
  }
129
202
  runClaude(["plugin", "enable", PLUGIN_KEY]);
130
- log(` Claude Code installed via marketplace ${MARKETPLACE_SOURCE}`);
131
203
  }
132
204
  function uninstallClaude() {
133
205
  try {
@@ -187,7 +259,15 @@ function buildHooksJson() {
187
259
  }
188
260
  };
189
261
  }
190
- function isHivemindHookEntry(entry) {
262
+ var HIVEMIND_BUNDLE_FILES = [
263
+ "session-start.js",
264
+ "session-start-setup.js",
265
+ "capture.js",
266
+ "pre-tool-use.js",
267
+ "stop.js",
268
+ "wiki-worker.js"
269
+ ];
270
+ function isHivemindHookEntry(entry, pluginDir = PLUGIN_DIR) {
191
271
  if (!entry || typeof entry !== "object")
192
272
  return false;
193
273
  const e = entry;
@@ -196,9 +276,41 @@ function isHivemindHookEntry(entry) {
196
276
  if (!h || typeof h !== "object")
197
277
  return false;
198
278
  const cmd = h.command;
199
- return typeof cmd === "string" && cmd.includes(`${PLUGIN_DIR}/bundle/`);
279
+ if (typeof cmd !== "string")
280
+ return false;
281
+ if (cmd.includes(`${pluginDir}/bundle/`))
282
+ return true;
283
+ return HIVEMIND_BUNDLE_FILES.some((f) => cmd.includes(`/bundle/${f}`));
284
+ });
285
+ }
286
+ function isForeignHivemindHookEntry(entry, pluginDir = PLUGIN_DIR) {
287
+ if (!isHivemindHookEntry(entry, pluginDir))
288
+ return false;
289
+ const e = entry;
290
+ const hooks = Array.isArray(e.hooks) ? e.hooks : [];
291
+ return hooks.every((h) => {
292
+ if (!h || typeof h !== "object")
293
+ return false;
294
+ const cmd = h.command;
295
+ if (typeof cmd !== "string")
296
+ return false;
297
+ return !cmd.includes(`${pluginDir}/bundle/`);
200
298
  });
201
299
  }
300
+ function mergeHooks(existing, ours, pluginDir = PLUGIN_DIR) {
301
+ const existingHooks = existing.hooks && typeof existing.hooks === "object" ? existing.hooks : {};
302
+ const ourHooks = ours.hooks;
303
+ const merged = {};
304
+ for (const [event, entries] of Object.entries(existingHooks)) {
305
+ const surviving = (entries ?? []).filter((e) => !isHivemindHookEntry(e, pluginDir));
306
+ if (surviving.length)
307
+ merged[event] = surviving;
308
+ }
309
+ for (const [event, entries] of Object.entries(ourHooks)) {
310
+ merged[event] = [...merged[event] ?? [], ...entries ?? []];
311
+ }
312
+ return { ...existing, hooks: merged };
313
+ }
202
314
  function mergeHooksJson(ours) {
203
315
  let existing = {};
204
316
  try {
@@ -210,18 +322,30 @@ function mergeHooksJson(ours) {
210
322
  } catch {
211
323
  warn(` Codex ${HOOKS_PATH} unparseable \u2014 ignoring prior content`);
212
324
  }
325
+ reportForeignHivemindHooks(existing);
326
+ return mergeHooks(existing, ours);
327
+ }
328
+ function reportForeignHivemindHooks(existing) {
213
329
  const existingHooks = existing.hooks && typeof existing.hooks === "object" ? existing.hooks : {};
214
- const ourHooks = ours.hooks;
215
- const merged = {};
216
- for (const [event, entries] of Object.entries(existingHooks)) {
217
- const surviving = (entries ?? []).filter((e) => !isHivemindHookEntry(e));
218
- if (surviving.length)
219
- merged[event] = surviving;
220
- }
221
- for (const [event, entries] of Object.entries(ourHooks)) {
222
- merged[event] = [...merged[event] ?? [], ...entries ?? []];
330
+ const foreign = /* @__PURE__ */ new Set();
331
+ for (const entries of Object.values(existingHooks)) {
332
+ for (const e of entries ?? []) {
333
+ if (!isForeignHivemindHookEntry(e))
334
+ continue;
335
+ const hooks = Array.isArray(e.hooks) ? e.hooks : [];
336
+ for (const h of hooks) {
337
+ const cmd = h?.command;
338
+ if (typeof cmd === "string")
339
+ foreign.add(cmd);
340
+ }
341
+ }
223
342
  }
224
- return { ...existing, hooks: merged };
343
+ if (foreign.size === 0)
344
+ return;
345
+ warn(` Codex stripping ${foreign.size} hivemind hook(s) from a non-canonical path:`);
346
+ for (const cmd of foreign)
347
+ warn(` ${cmd}`);
348
+ warn(` (these were probably leftover from a local dev clone \u2014 re-add them manually if intentional)`);
225
349
  }
226
350
  function tryEnableCodexHooks() {
227
351
  try {
@@ -253,8 +377,28 @@ function installCodex() {
253
377
  }
254
378
  function uninstallCodex() {
255
379
  if (existsSync2(HOOKS_PATH)) {
256
- unlinkSync2(HOOKS_PATH);
257
- log(` Codex removed ${HOOKS_PATH}`);
380
+ let existing = {};
381
+ try {
382
+ const raw = JSON.parse(readFileSync3(HOOKS_PATH, "utf-8"));
383
+ if (raw && typeof raw === "object")
384
+ existing = raw;
385
+ } catch {
386
+ unlinkSync2(HOOKS_PATH);
387
+ log(` Codex removed unparseable ${HOOKS_PATH}`);
388
+ existing = {};
389
+ }
390
+ if (Object.keys(existing).length > 0) {
391
+ const stripped = mergeHooks(existing, { hooks: {} });
392
+ const survivingHooks = stripped.hooks ?? {};
393
+ const otherTopLevelKeys = Object.keys(stripped).filter((k) => k !== "hooks");
394
+ if (Object.keys(survivingHooks).length === 0 && otherTopLevelKeys.length === 0) {
395
+ unlinkSync2(HOOKS_PATH);
396
+ log(` Codex removed ${HOOKS_PATH}`);
397
+ } else {
398
+ writeJson(HOOKS_PATH, stripped);
399
+ log(` Codex stripped hivemind hooks from ${HOOKS_PATH}`);
400
+ }
401
+ }
258
402
  }
259
403
  if (existsSync2(SKILL_LINK)) {
260
404
  unlinkSync2(SKILL_LINK);
@@ -336,7 +480,7 @@ function isHivemindEntry(entry) {
336
480
  const cmd = entry.command;
337
481
  return typeof cmd === "string" && cmd.includes("/.cursor/hivemind/bundle/");
338
482
  }
339
- function mergeHooks(existing) {
483
+ function mergeHooks2(existing) {
340
484
  const root = existing ?? { version: 1, hooks: {} };
341
485
  if (!root.version)
342
486
  root.version = 1;
@@ -375,7 +519,7 @@ function installCursor() {
375
519
  ensureDir(PLUGIN_DIR3);
376
520
  copyDir(srcBundle, join5(PLUGIN_DIR3, "bundle"));
377
521
  const existing = readJson(HOOKS_PATH2);
378
- const merged = mergeHooks(existing);
522
+ const merged = mergeHooks2(existing);
379
523
  writeJson(HOOKS_PATH2, merged);
380
524
  writeVersionStamp(PLUGIN_DIR3, getVersion());
381
525
  log(` Cursor installed -> ${PLUGIN_DIR3}`);
@@ -3090,7 +3234,7 @@ function buildHooksBlock() {
3090
3234
  on_session_end: [buildHookEntry("session-end.js", 30)]
3091
3235
  };
3092
3236
  }
3093
- function mergeHooks2(existing) {
3237
+ function mergeHooks3(existing) {
3094
3238
  const merged = { ...existing ?? {} };
3095
3239
  const ours = buildHooksBlock();
3096
3240
  for (const [event, entries] of Object.entries(ours)) {
@@ -3151,7 +3295,7 @@ function installHermes() {
3151
3295
  command: "node",
3152
3296
  args: [MCP_SERVER_PATH]
3153
3297
  };
3154
- cfg.hooks = mergeHooks2(cfg.hooks);
3298
+ cfg.hooks = mergeHooks3(cfg.hooks);
3155
3299
  cfg.hooks_auto_accept = true;
3156
3300
  writeConfig(cfg);
3157
3301
  log(` Hermes config updated -> ${CONFIG_PATH} (mcp_servers + hooks + hooks_auto_accept)`);
@@ -3182,6 +3326,10 @@ function uninstallHermes() {
3182
3326
  delete cfg.hooks;
3183
3327
  touched = true;
3184
3328
  }
3329
+ if ("hooks_auto_accept" in cfg) {
3330
+ delete cfg.hooks_auto_accept;
3331
+ touched = true;
3332
+ }
3185
3333
  if (touched) {
3186
3334
  if (Object.keys(cfg).length === 0) {
3187
3335
  unlinkSync4(CONFIG_PATH);
@@ -3202,6 +3350,9 @@ var LEGACY_SKILL_DIR = join8(PI_AGENT_DIR, "skills", "hivemind-memory");
3202
3350
  var EXTENSIONS_DIR = join8(PI_AGENT_DIR, "extensions");
3203
3351
  var EXTENSION_PATH = join8(EXTENSIONS_DIR, "hivemind.ts");
3204
3352
  var VERSION_DIR = join8(PI_AGENT_DIR, ".hivemind");
3353
+ var WIKI_WORKER_DIR = join8(PI_AGENT_DIR, "hivemind");
3354
+ var WIKI_WORKER_PATH = join8(WIKI_WORKER_DIR, "wiki-worker.js");
3355
+ var SKILIFY_WORKER_PATH = join8(WIKI_WORKER_DIR, "skilify-worker.js");
3205
3356
  var HIVEMIND_BLOCK_START = "<!-- BEGIN hivemind-memory -->";
3206
3357
  var HIVEMIND_BLOCK_END = "<!-- END hivemind-memory -->";
3207
3358
  var HIVEMIND_BLOCK_BODY = `${HIVEMIND_BLOCK_START}
@@ -3281,10 +3432,26 @@ function installPi() {
3281
3432
  }
3282
3433
  ensureDir(EXTENSIONS_DIR);
3283
3434
  copyFileSync2(srcExtension, EXTENSION_PATH);
3435
+ const srcWorker = join8(pkgRoot(), "pi", "bundle", "wiki-worker.js");
3436
+ if (existsSync7(srcWorker)) {
3437
+ ensureDir(WIKI_WORKER_DIR);
3438
+ copyFileSync2(srcWorker, WIKI_WORKER_PATH);
3439
+ }
3440
+ const srcSkilifyWorker = join8(pkgRoot(), "pi", "bundle", "skilify-worker.js");
3441
+ if (existsSync7(srcSkilifyWorker)) {
3442
+ ensureDir(WIKI_WORKER_DIR);
3443
+ copyFileSync2(srcSkilifyWorker, SKILIFY_WORKER_PATH);
3444
+ }
3284
3445
  ensureDir(VERSION_DIR);
3285
3446
  writeVersionStamp(VERSION_DIR, getVersion());
3286
3447
  log(` pi AGENTS.md updated -> ${AGENTS_MD}`);
3287
3448
  log(` pi extension installed -> ${EXTENSION_PATH}`);
3449
+ if (existsSync7(WIKI_WORKER_PATH)) {
3450
+ log(` pi wiki-worker installed -> ${WIKI_WORKER_PATH}`);
3451
+ }
3452
+ if (existsSync7(SKILIFY_WORKER_PATH)) {
3453
+ log(` pi skilify-worker installed -> ${SKILIFY_WORKER_PATH}`);
3454
+ }
3288
3455
  }
3289
3456
  function uninstallPi() {
3290
3457
  if (existsSync7(LEGACY_SKILL_DIR)) {
@@ -3295,6 +3462,10 @@ function uninstallPi() {
3295
3462
  rmSync3(EXTENSION_PATH, { force: true });
3296
3463
  log(` pi removed extension ${EXTENSION_PATH}`);
3297
3464
  }
3465
+ if (existsSync7(WIKI_WORKER_DIR)) {
3466
+ rmSync3(WIKI_WORKER_DIR, { recursive: true, force: true });
3467
+ log(` pi removed wiki-worker dir ${WIKI_WORKER_DIR}`);
3468
+ }
3298
3469
  if (existsSync7(AGENTS_MD)) {
3299
3470
  const prior = readFileSync5(AGENTS_MD, "utf-8");
3300
3471
  const stripped = stripHivemindBlock(prior);
@@ -3311,43 +3482,228 @@ function uninstallPi() {
3311
3482
  }
3312
3483
  }
3313
3484
 
3485
+ // dist/src/cli/embeddings.js
3486
+ import { copyFileSync as copyFileSync3, chmodSync, existsSync as existsSync8, lstatSync as lstatSync2, readdirSync, readlinkSync, rmSync as rmSync4, statSync, unlinkSync as unlinkSync5 } from "node:fs";
3487
+ import { execFileSync as execFileSync3 } from "node:child_process";
3488
+ import { join as join9 } from "node:path";
3489
+ var SHARED_DIR = join9(HOME, ".hivemind", "embed-deps");
3490
+ var SHARED_NODE_MODULES = join9(SHARED_DIR, "node_modules");
3491
+ var SHARED_DAEMON_PATH = join9(SHARED_DIR, "embed-daemon.js");
3492
+ var TRANSFORMERS_PKG = "@huggingface/transformers";
3493
+ var TRANSFORMERS_RANGE = "^3.0.0";
3494
+ function findHivemindInstalls(home = HOME) {
3495
+ const out = [];
3496
+ const fixed = [
3497
+ { id: "codex", pluginDir: join9(home, ".codex", "hivemind") },
3498
+ { id: "cursor", pluginDir: join9(home, ".cursor", "hivemind") },
3499
+ { id: "hermes", pluginDir: join9(home, ".hermes", "hivemind") }
3500
+ ];
3501
+ for (const inst of fixed) {
3502
+ if (existsSync8(join9(inst.pluginDir, "bundle")))
3503
+ out.push(inst);
3504
+ }
3505
+ const ccCache = join9(home, ".claude", "plugins", "cache", "hivemind", "hivemind");
3506
+ if (existsSync8(ccCache)) {
3507
+ let entries = [];
3508
+ try {
3509
+ entries = readdirSync(ccCache);
3510
+ } catch {
3511
+ }
3512
+ for (const ver of entries) {
3513
+ const dir = join9(ccCache, ver);
3514
+ try {
3515
+ if (!statSync(dir).isDirectory())
3516
+ continue;
3517
+ } catch {
3518
+ continue;
3519
+ }
3520
+ const candidates = [join9(dir, "bundle"), join9(dir, "claude-code", "bundle")];
3521
+ if (candidates.some((p) => existsSync8(p))) {
3522
+ out.push({ id: `claude (${ver})`, pluginDir: dir });
3523
+ }
3524
+ }
3525
+ }
3526
+ return out;
3527
+ }
3528
+ function isSharedDepsInstalled(sharedNodeModules = SHARED_NODE_MODULES) {
3529
+ return existsSync8(join9(sharedNodeModules, TRANSFORMERS_PKG));
3530
+ }
3531
+ function isSymlinkToSharedDeps(linkPath, sharedNodeModules) {
3532
+ if (!existsSync8(linkPath))
3533
+ return false;
3534
+ try {
3535
+ if (!lstatSync2(linkPath).isSymbolicLink())
3536
+ return false;
3537
+ return readlinkSync(linkPath) === sharedNodeModules;
3538
+ } catch {
3539
+ return false;
3540
+ }
3541
+ }
3542
+ function linkStateFor(install, sharedNodeModules = SHARED_NODE_MODULES) {
3543
+ const link = join9(install.pluginDir, "node_modules");
3544
+ if (!existsSync8(link) && !isSymbolicLink(link))
3545
+ return { kind: "no-node-modules" };
3546
+ try {
3547
+ if (lstatSync2(link).isSymbolicLink()) {
3548
+ const target = readlinkSync(link);
3549
+ return target === sharedNodeModules ? { kind: "linked-to-shared" } : { kind: "linked-elsewhere", target };
3550
+ }
3551
+ } catch {
3552
+ return { kind: "no-node-modules" };
3553
+ }
3554
+ return { kind: "owns-own-node-modules" };
3555
+ }
3556
+ function isSymbolicLink(path) {
3557
+ try {
3558
+ return lstatSync2(path).isSymbolicLink();
3559
+ } catch {
3560
+ return false;
3561
+ }
3562
+ }
3563
+ function ensureSharedDeps() {
3564
+ if (!isSharedDepsInstalled()) {
3565
+ log(` Embeddings installing ${TRANSFORMERS_PKG}@${TRANSFORMERS_RANGE} into ${SHARED_DIR}`);
3566
+ log(` (~600 MB; first install only \u2014 every agent will share this)`);
3567
+ ensureDir(SHARED_DIR);
3568
+ writeJson(join9(SHARED_DIR, "package.json"), {
3569
+ name: "hivemind-embed-deps",
3570
+ version: "1.0.0",
3571
+ private: true,
3572
+ dependencies: { [TRANSFORMERS_PKG]: TRANSFORMERS_RANGE }
3573
+ });
3574
+ execFileSync3("npm", ["install", "--omit=dev", "--no-package-lock", "--no-audit", "--no-fund"], {
3575
+ cwd: SHARED_DIR,
3576
+ stdio: "inherit"
3577
+ });
3578
+ } else {
3579
+ log(` Embeddings shared deps already present at ${SHARED_DIR}`);
3580
+ }
3581
+ ensureDir(SHARED_DIR);
3582
+ const src = join9(pkgRoot(), "embeddings", "embed-daemon.js");
3583
+ if (existsSync8(src)) {
3584
+ copyFileSync3(src, SHARED_DAEMON_PATH);
3585
+ chmodSync(SHARED_DAEMON_PATH, 493);
3586
+ } else {
3587
+ warn(` Embeddings standalone daemon bundle missing at ${src} (run 'npm run build' first)`);
3588
+ }
3589
+ }
3590
+ function linkAgent(install) {
3591
+ const link = join9(install.pluginDir, "node_modules");
3592
+ symlinkForce(SHARED_NODE_MODULES, link);
3593
+ log(` Embeddings linked ${install.id.padEnd(20)} -> shared deps`);
3594
+ }
3595
+ function enableEmbeddings() {
3596
+ ensureSharedDeps();
3597
+ const installs = findHivemindInstalls();
3598
+ if (installs.length === 0) {
3599
+ warn(" Embeddings no hivemind installs detected \u2014 run `hivemind install` first");
3600
+ warn(" (the shared deps are in place; subsequent agent installs will pick them up if you re-run `hivemind embeddings install`)");
3601
+ return;
3602
+ }
3603
+ for (const inst of installs)
3604
+ linkAgent(inst);
3605
+ log(` Embeddings enabled. Restart your agents to pick up.`);
3606
+ }
3607
+ function disableEmbeddings(opts) {
3608
+ const installs = findHivemindInstalls();
3609
+ for (const inst of installs) {
3610
+ const link = join9(inst.pluginDir, "node_modules");
3611
+ if (isSymlinkToSharedDeps(link, SHARED_NODE_MODULES)) {
3612
+ unlinkSync5(link);
3613
+ log(` Embeddings unlinked ${inst.id}`);
3614
+ }
3615
+ }
3616
+ if (opts?.prune && existsSync8(SHARED_DIR)) {
3617
+ rmSync4(SHARED_DIR, { recursive: true, force: true });
3618
+ log(` Embeddings pruned ${SHARED_DIR}`);
3619
+ }
3620
+ }
3621
+ function statusEmbeddings() {
3622
+ log(`Shared deps: ${SHARED_DIR}`);
3623
+ log(`Installed: ${isSharedDepsInstalled() ? "yes" : "no"}`);
3624
+ log(`Daemon: ${existsSync8(SHARED_DAEMON_PATH) ? SHARED_DAEMON_PATH : "(not present)"}`);
3625
+ log("");
3626
+ log(`Agent installs:`);
3627
+ const installs = findHivemindInstalls();
3628
+ if (installs.length === 0) {
3629
+ log(` (none detected)`);
3630
+ return;
3631
+ }
3632
+ for (const inst of installs) {
3633
+ const state = linkStateFor(inst);
3634
+ let label;
3635
+ switch (state.kind) {
3636
+ case "linked-to-shared":
3637
+ label = "\u2713 linked \u2192 shared";
3638
+ break;
3639
+ case "no-node-modules":
3640
+ label = "\u2717 not linked (embeddings disabled)";
3641
+ break;
3642
+ case "owns-own-node-modules":
3643
+ label = "\u25B3 has its own node_modules (not shared)";
3644
+ break;
3645
+ case "linked-elsewhere":
3646
+ label = `\u25B3 linked \u2192 ${state.target}`;
3647
+ break;
3648
+ }
3649
+ log(` ${inst.id.padEnd(20)} ${label}`);
3650
+ log(` ${" ".repeat(20)} ${inst.pluginDir}`);
3651
+ }
3652
+ }
3653
+
3314
3654
  // dist/src/cli/auth.js
3315
3655
  import { existsSync as existsSync9 } from "node:fs";
3316
- import { join as join10 } from "node:path";
3656
+ import { join as join11 } from "node:path";
3317
3657
 
3318
3658
  // dist/src/commands/auth.js
3319
- import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, existsSync as existsSync8, mkdirSync as mkdirSync2, unlinkSync as unlinkSync5 } from "node:fs";
3320
- import { join as join9 } from "node:path";
3321
- import { homedir as homedir2 } from "node:os";
3322
3659
  import { execSync } from "node:child_process";
3323
- var CONFIG_DIR = join9(homedir2(), ".deeplake");
3324
- var CREDS_PATH = join9(CONFIG_DIR, "credentials.json");
3325
- var DEFAULT_API_URL = "https://api.deeplake.ai";
3660
+
3661
+ // dist/src/utils/client-header.js
3662
+ var DEEPLAKE_CLIENT_HEADER = "X-Deeplake-Client";
3663
+ function deeplakeClientValue() {
3664
+ return "hivemind";
3665
+ }
3666
+ function deeplakeClientHeader() {
3667
+ return { [DEEPLAKE_CLIENT_HEADER]: deeplakeClientValue() };
3668
+ }
3669
+
3670
+ // dist/src/commands/auth-creds.js
3671
+ import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, mkdirSync as mkdirSync2, unlinkSync as unlinkSync6 } from "node:fs";
3672
+ import { join as join10 } from "node:path";
3673
+ import { homedir as homedir2 } from "node:os";
3674
+ function configDir() {
3675
+ return join10(homedir2(), ".deeplake");
3676
+ }
3677
+ function credsPath() {
3678
+ return join10(configDir(), "credentials.json");
3679
+ }
3326
3680
  function loadCredentials() {
3327
- if (!existsSync8(CREDS_PATH))
3328
- return null;
3329
3681
  try {
3330
- return JSON.parse(readFileSync6(CREDS_PATH, "utf-8"));
3682
+ return JSON.parse(readFileSync6(credsPath(), "utf-8"));
3331
3683
  } catch {
3332
3684
  return null;
3333
3685
  }
3334
3686
  }
3335
3687
  function saveCredentials(creds) {
3336
- if (!existsSync8(CONFIG_DIR))
3337
- mkdirSync2(CONFIG_DIR, { recursive: true, mode: 448 });
3338
- writeFileSync4(CREDS_PATH, JSON.stringify({ ...creds, savedAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2), { mode: 384 });
3688
+ mkdirSync2(configDir(), { recursive: true, mode: 448 });
3689
+ writeFileSync4(credsPath(), JSON.stringify({ ...creds, savedAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2), { mode: 384 });
3339
3690
  }
3340
3691
  function deleteCredentials() {
3341
- if (existsSync8(CREDS_PATH)) {
3342
- unlinkSync5(CREDS_PATH);
3692
+ try {
3693
+ unlinkSync6(credsPath());
3343
3694
  return true;
3695
+ } catch {
3696
+ return false;
3344
3697
  }
3345
- return false;
3346
3698
  }
3699
+
3700
+ // dist/src/commands/auth.js
3701
+ var DEFAULT_API_URL = "https://api.deeplake.ai";
3347
3702
  async function apiGet(path, token, apiUrl, orgId) {
3348
3703
  const headers = {
3349
3704
  Authorization: `Bearer ${token}`,
3350
- "Content-Type": "application/json"
3705
+ "Content-Type": "application/json",
3706
+ ...deeplakeClientHeader()
3351
3707
  };
3352
3708
  if (orgId)
3353
3709
  headers["X-Activeloop-Org-Id"] = orgId;
@@ -3359,7 +3715,8 @@ async function apiGet(path, token, apiUrl, orgId) {
3359
3715
  async function apiPost(path, body, token, apiUrl, orgId) {
3360
3716
  const headers = {
3361
3717
  Authorization: `Bearer ${token}`,
3362
- "Content-Type": "application/json"
3718
+ "Content-Type": "application/json",
3719
+ ...deeplakeClientHeader()
3363
3720
  };
3364
3721
  if (orgId)
3365
3722
  headers["X-Activeloop-Org-Id"] = orgId;
@@ -3371,7 +3728,8 @@ async function apiPost(path, body, token, apiUrl, orgId) {
3371
3728
  async function apiDelete(path, token, apiUrl, orgId) {
3372
3729
  const headers = {
3373
3730
  Authorization: `Bearer ${token}`,
3374
- "Content-Type": "application/json"
3731
+ "Content-Type": "application/json",
3732
+ ...deeplakeClientHeader()
3375
3733
  };
3376
3734
  if (orgId)
3377
3735
  headers["X-Activeloop-Org-Id"] = orgId;
@@ -3382,7 +3740,7 @@ async function apiDelete(path, token, apiUrl, orgId) {
3382
3740
  async function requestDeviceCode(apiUrl = DEFAULT_API_URL) {
3383
3741
  const resp = await fetch(`${apiUrl}/auth/device/code`, {
3384
3742
  method: "POST",
3385
- headers: { "Content-Type": "application/json" }
3743
+ headers: { "Content-Type": "application/json", ...deeplakeClientHeader() }
3386
3744
  });
3387
3745
  if (!resp.ok)
3388
3746
  throw new Error(`Device flow unavailable: HTTP ${resp.status}`);
@@ -3391,7 +3749,7 @@ async function requestDeviceCode(apiUrl = DEFAULT_API_URL) {
3391
3749
  async function pollForToken(deviceCode, apiUrl = DEFAULT_API_URL) {
3392
3750
  const resp = await fetch(`${apiUrl}/auth/device/token`, {
3393
3751
  method: "POST",
3394
- headers: { "Content-Type": "application/json" },
3752
+ headers: { "Content-Type": "application/json", ...deeplakeClientHeader() },
3395
3753
  body: JSON.stringify({ device_code: deviceCode })
3396
3754
  });
3397
3755
  if (resp.ok)
@@ -3517,9 +3875,9 @@ Using: ${orgName}
3517
3875
  }
3518
3876
 
3519
3877
  // dist/src/cli/auth.js
3520
- var CREDS_PATH2 = join10(HOME, ".deeplake", "credentials.json");
3878
+ var CREDS_PATH = join11(HOME, ".deeplake", "credentials.json");
3521
3879
  function isLoggedIn() {
3522
- return existsSync9(CREDS_PATH2) && loadCredentials() !== null;
3880
+ return existsSync9(CREDS_PATH) && loadCredentials() !== null;
3523
3881
  }
3524
3882
  async function ensureLoggedIn() {
3525
3883
  if (isLoggedIn())
@@ -3553,11 +3911,11 @@ async function maybeShowOrgChoice() {
3553
3911
 
3554
3912
  // dist/src/config.js
3555
3913
  import { readFileSync as readFileSync7, existsSync as existsSync10 } from "node:fs";
3556
- import { join as join11 } from "node:path";
3914
+ import { join as join12 } from "node:path";
3557
3915
  import { homedir as homedir3, userInfo } from "node:os";
3558
3916
  function loadConfig() {
3559
3917
  const home = homedir3();
3560
- const credPath = join11(home, ".deeplake", "credentials.json");
3918
+ const credPath = join12(home, ".deeplake", "credentials.json");
3561
3919
  let creds = null;
3562
3920
  if (existsSync10(credPath)) {
3563
3921
  try {
@@ -3579,22 +3937,20 @@ function loadConfig() {
3579
3937
  apiUrl: process.env.HIVEMIND_API_URL ?? creds?.apiUrl ?? "https://api.deeplake.ai",
3580
3938
  tableName: process.env.HIVEMIND_TABLE ?? "memory",
3581
3939
  sessionsTableName: process.env.HIVEMIND_SESSIONS_TABLE ?? "sessions",
3582
- memoryPath: process.env.HIVEMIND_MEMORY_PATH ?? join11(home, ".deeplake", "memory")
3940
+ skillsTableName: process.env.HIVEMIND_SKILLS_TABLE ?? "skills",
3941
+ memoryPath: process.env.HIVEMIND_MEMORY_PATH ?? join12(home, ".deeplake", "memory")
3583
3942
  };
3584
3943
  }
3585
3944
 
3586
3945
  // dist/src/deeplake-api.js
3587
3946
  import { randomUUID } from "node:crypto";
3588
- import { existsSync as existsSync11, mkdirSync as mkdirSync3, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "node:fs";
3589
- import { join as join13 } from "node:path";
3590
- import { tmpdir } from "node:os";
3591
3947
 
3592
3948
  // dist/src/utils/debug.js
3593
3949
  import { appendFileSync } from "node:fs";
3594
- import { join as join12 } from "node:path";
3950
+ import { join as join13 } from "node:path";
3595
3951
  import { homedir as homedir4 } from "node:os";
3596
3952
  var DEBUG = process.env.HIVEMIND_DEBUG === "1";
3597
- var LOG = join12(homedir4(), ".deeplake", "hook-debug.log");
3953
+ var LOG = join13(homedir4(), ".deeplake", "hook-debug.log");
3598
3954
  function log2(tag, msg) {
3599
3955
  if (!DEBUG)
3600
3956
  return;
@@ -3606,8 +3962,24 @@ function log2(tag, msg) {
3606
3962
  function sqlStr(value) {
3607
3963
  return value.replace(/\\/g, "\\\\").replace(/'/g, "''").replace(/\0/g, "").replace(/[\x01-\x08\x0b\x0c\x0e-\x1f\x7f]/g, "");
3608
3964
  }
3965
+ function sqlIdent(name) {
3966
+ if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name)) {
3967
+ throw new Error(`Invalid SQL identifier: ${JSON.stringify(name)}`);
3968
+ }
3969
+ return name;
3970
+ }
3971
+
3972
+ // dist/src/embeddings/columns.js
3973
+ var SUMMARY_EMBEDDING_COL = "summary_embedding";
3974
+ var MESSAGE_EMBEDDING_COL = "message_embedding";
3609
3975
 
3610
3976
  // dist/src/deeplake-api.js
3977
+ var indexMarkerStorePromise = null;
3978
+ function getIndexMarkerStore() {
3979
+ if (!indexMarkerStorePromise)
3980
+ indexMarkerStorePromise = Promise.resolve().then(() => (init_index_marker_store(), index_marker_store_exports));
3981
+ return indexMarkerStorePromise;
3982
+ }
3611
3983
  var log3 = (msg) => log2("sdk", msg);
3612
3984
  function summarizeSql(sql, maxLen = 220) {
3613
3985
  const compact = sql.replace(/\s+/g, " ").trim();
@@ -3627,7 +3999,6 @@ var MAX_RETRIES = 3;
3627
3999
  var BASE_DELAY_MS = 500;
3628
4000
  var MAX_CONCURRENCY = 5;
3629
4001
  var QUERY_TIMEOUT_MS = Number(process.env.HIVEMIND_QUERY_TIMEOUT_MS ?? 1e4);
3630
- var INDEX_MARKER_TTL_MS = Number(process.env.HIVEMIND_INDEX_MARKER_TTL_MS ?? 6 * 60 * 6e4);
3631
4002
  function sleep(ms) {
3632
4003
  return new Promise((resolve) => setTimeout(resolve, ms));
3633
4004
  }
@@ -3647,9 +4018,6 @@ function isTransientHtml403(text) {
3647
4018
  const body = text.toLowerCase();
3648
4019
  return body.includes("<html") || body.includes("403 forbidden") || body.includes("cloudflare") || body.includes("nginx");
3649
4020
  }
3650
- function getIndexMarkerDir() {
3651
- return process.env.HIVEMIND_INDEX_MARKER_DIR ?? join13(tmpdir(), "hivemind-deeplake-indexes");
3652
- }
3653
4021
  var Semaphore = class {
3654
4022
  max;
3655
4023
  waiting = [];
@@ -3718,7 +4086,8 @@ var DeeplakeApi = class {
3718
4086
  headers: {
3719
4087
  Authorization: `Bearer ${this.token}`,
3720
4088
  "Content-Type": "application/json",
3721
- "X-Activeloop-Org-Id": this.orgId
4089
+ "X-Activeloop-Org-Id": this.orgId,
4090
+ ...deeplakeClientHeader()
3722
4091
  },
3723
4092
  signal,
3724
4093
  body: JSON.stringify({ query: sql })
@@ -3745,7 +4114,8 @@ var DeeplakeApi = class {
3745
4114
  }
3746
4115
  const text = await resp.text().catch(() => "");
3747
4116
  const retryable403 = isSessionInsertQuery(sql) && (resp.status === 401 || resp.status === 403 && (text.length === 0 || isTransientHtml403(text)));
3748
- if (attempt < MAX_RETRIES && (RETRYABLE_CODES.has(resp.status) || retryable403)) {
4117
+ const alreadyExists = resp.status === 500 && isDuplicateIndexError(text);
4118
+ if (!alreadyExists && attempt < MAX_RETRIES && (RETRYABLE_CODES.has(resp.status) || retryable403)) {
3749
4119
  const delay = BASE_DELAY_MS * Math.pow(2, attempt) + Math.random() * 200;
3750
4120
  log3(`query retry ${attempt + 1}/${MAX_RETRIES} (${resp.status}) in ${delay.toFixed(0)}ms`);
3751
4121
  await sleep(delay);
@@ -3779,7 +4149,7 @@ var DeeplakeApi = class {
3779
4149
  const lud = row.lastUpdateDate ?? ts;
3780
4150
  const exists = await this.query(`SELECT path FROM "${this.tableName}" WHERE path = '${sqlStr(row.path)}' LIMIT 1`);
3781
4151
  if (exists.length > 0) {
3782
- let setClauses = `summary = E'${sqlStr(row.contentText)}', mime_type = '${sqlStr(row.mimeType)}', size_bytes = ${row.sizeBytes}, last_update_date = '${lud}'`;
4152
+ let setClauses = `summary = E'${sqlStr(row.contentText)}', ${SUMMARY_EMBEDDING_COL} = NULL, mime_type = '${sqlStr(row.mimeType)}', size_bytes = ${row.sizeBytes}, last_update_date = '${lud}'`;
3783
4153
  if (row.project !== void 0)
3784
4154
  setClauses += `, project = '${sqlStr(row.project)}'`;
3785
4155
  if (row.description !== void 0)
@@ -3787,8 +4157,8 @@ var DeeplakeApi = class {
3787
4157
  await this.query(`UPDATE "${this.tableName}" SET ${setClauses} WHERE path = '${sqlStr(row.path)}'`);
3788
4158
  } else {
3789
4159
  const id = randomUUID();
3790
- let cols = "id, path, filename, summary, mime_type, size_bytes, creation_date, last_update_date";
3791
- let vals = `'${id}', '${sqlStr(row.path)}', '${sqlStr(row.filename)}', E'${sqlStr(row.contentText)}', '${sqlStr(row.mimeType)}', ${row.sizeBytes}, '${cd}', '${lud}'`;
4160
+ let cols = `id, path, filename, summary, ${SUMMARY_EMBEDDING_COL}, mime_type, size_bytes, creation_date, last_update_date`;
4161
+ let vals = `'${id}', '${sqlStr(row.path)}', '${sqlStr(row.filename)}', E'${sqlStr(row.contentText)}', NULL, '${sqlStr(row.mimeType)}', ${row.sizeBytes}, '${cd}', '${lud}'`;
3792
4162
  if (row.project !== void 0) {
3793
4163
  cols += ", project";
3794
4164
  vals += `, '${sqlStr(row.project)}'`;
@@ -3813,48 +4183,83 @@ var DeeplakeApi = class {
3813
4183
  buildLookupIndexName(table, suffix) {
3814
4184
  return `idx_${table}_${suffix}`.replace(/[^a-zA-Z0-9_]/g, "_");
3815
4185
  }
3816
- getLookupIndexMarkerPath(table, suffix) {
3817
- const markerKey = [
3818
- this.workspaceId,
3819
- this.orgId,
3820
- table,
3821
- suffix
3822
- ].join("__").replace(/[^a-zA-Z0-9_.-]/g, "_");
3823
- return join13(getIndexMarkerDir(), `${markerKey}.json`);
3824
- }
3825
- hasFreshLookupIndexMarker(table, suffix) {
3826
- const markerPath = this.getLookupIndexMarkerPath(table, suffix);
3827
- if (!existsSync11(markerPath))
3828
- return false;
3829
- try {
3830
- const raw = JSON.parse(readFileSync8(markerPath, "utf-8"));
3831
- const updatedAt = raw.updatedAt ? new Date(raw.updatedAt).getTime() : NaN;
3832
- if (!Number.isFinite(updatedAt) || Date.now() - updatedAt > INDEX_MARKER_TTL_MS)
3833
- return false;
3834
- return true;
3835
- } catch {
3836
- return false;
3837
- }
3838
- }
3839
- markLookupIndexReady(table, suffix) {
3840
- mkdirSync3(getIndexMarkerDir(), { recursive: true });
3841
- writeFileSync5(this.getLookupIndexMarkerPath(table, suffix), JSON.stringify({ updatedAt: (/* @__PURE__ */ new Date()).toISOString() }), "utf-8");
3842
- }
3843
4186
  async ensureLookupIndex(table, suffix, columnsSql) {
3844
- if (this.hasFreshLookupIndexMarker(table, suffix))
4187
+ const markers = await getIndexMarkerStore();
4188
+ const markerPath = markers.buildIndexMarkerPath(this.workspaceId, this.orgId, table, suffix);
4189
+ if (markers.hasFreshIndexMarker(markerPath))
3845
4190
  return;
3846
4191
  const indexName = this.buildLookupIndexName(table, suffix);
3847
4192
  try {
3848
4193
  await this.query(`CREATE INDEX IF NOT EXISTS "${indexName}" ON "${table}" ${columnsSql}`);
3849
- this.markLookupIndexReady(table, suffix);
4194
+ markers.writeIndexMarker(markerPath);
3850
4195
  } catch (e) {
3851
4196
  if (isDuplicateIndexError(e)) {
3852
- this.markLookupIndexReady(table, suffix);
4197
+ markers.writeIndexMarker(markerPath);
3853
4198
  return;
3854
4199
  }
3855
4200
  log3(`index "${indexName}" skipped: ${e.message}`);
3856
4201
  }
3857
4202
  }
4203
+ /**
4204
+ * Ensure a vector column exists on the given table.
4205
+ *
4206
+ * The previous implementation always issued `ALTER TABLE ADD COLUMN IF NOT
4207
+ * EXISTS …` on every SessionStart. On a long-running workspace that's
4208
+ * already migrated, every call returns 500 "Column already exists" — noisy
4209
+ * in the log and a wasted round-trip. Worse, the very first call after the
4210
+ * column is genuinely added triggers Deeplake's post-ALTER `vector::at`
4211
+ * window (~30s) during which subsequent INSERTs fail; minimising the
4212
+ * number of ALTER calls minimises exposure to that window.
4213
+ *
4214
+ * New flow:
4215
+ * 1. Check the local marker file (mirrors ensureLookupIndex). If fresh,
4216
+ * return — zero network calls.
4217
+ * 2. SELECT 1 FROM information_schema.columns WHERE table_name = T AND
4218
+ * column_name = C. Read-only, idempotent, can't tickle the post-ALTER
4219
+ * bug. If the column is present → mark + return.
4220
+ * 3. Only if step 2 says the column is missing, fall back to ALTER ADD
4221
+ * COLUMN IF NOT EXISTS. Mark on success, also mark if Deeplake reports
4222
+ * "already exists" (race: another client added it between our SELECT
4223
+ * and ALTER).
4224
+ *
4225
+ * Marker uses the same dir / TTL as ensureLookupIndex so both schema
4226
+ * caches share an opt-out (HIVEMIND_INDEX_MARKER_DIR) and a TTL knob.
4227
+ */
4228
+ async ensureEmbeddingColumn(table, column) {
4229
+ await this.ensureColumn(table, column, "FLOAT4[]");
4230
+ }
4231
+ /**
4232
+ * Generic marker-gated column migration. Same SELECT-then-ALTER flow as
4233
+ * ensureEmbeddingColumn, parameterized by SQL type so it can patch up any
4234
+ * column that was added to the schema after the table was originally
4235
+ * created. Used today for `summary_embedding`, `message_embedding`, and
4236
+ * the `agent` column (added 2026-04-11) — the latter has no fallback if
4237
+ * a user upgraded over a pre-2026-04-11 table, so every INSERT fails
4238
+ * with `column "agent" does not exist`.
4239
+ */
4240
+ async ensureColumn(table, column, sqlType) {
4241
+ const markers = await getIndexMarkerStore();
4242
+ const markerPath = markers.buildIndexMarkerPath(this.workspaceId, this.orgId, table, `col_${column}`);
4243
+ if (markers.hasFreshIndexMarker(markerPath))
4244
+ return;
4245
+ const colCheck = `SELECT 1 FROM information_schema.columns WHERE table_name = '${sqlStr(table)}' AND column_name = '${sqlStr(column)}' AND table_schema = '${sqlStr(this.workspaceId)}' LIMIT 1`;
4246
+ const rows = await this.query(colCheck);
4247
+ if (rows.length > 0) {
4248
+ markers.writeIndexMarker(markerPath);
4249
+ return;
4250
+ }
4251
+ try {
4252
+ await this.query(`ALTER TABLE "${table}" ADD COLUMN ${column} ${sqlType}`);
4253
+ } catch (e) {
4254
+ const msg = e instanceof Error ? e.message : String(e);
4255
+ if (!/already exists/i.test(msg))
4256
+ throw e;
4257
+ const recheck = await this.query(colCheck);
4258
+ if (recheck.length === 0)
4259
+ throw e;
4260
+ }
4261
+ markers.writeIndexMarker(markerPath);
4262
+ }
3858
4263
  /** List all tables in the workspace (with retry). */
3859
4264
  async listTables(forceRefresh = false) {
3860
4265
  if (!forceRefresh && this._tablesCache)
@@ -3870,7 +4275,8 @@ var DeeplakeApi = class {
3870
4275
  const resp = await fetch(`${this.apiUrl}/workspaces/${this.workspaceId}/tables`, {
3871
4276
  headers: {
3872
4277
  Authorization: `Bearer ${this.token}`,
3873
- "X-Activeloop-Org-Id": this.orgId
4278
+ "X-Activeloop-Org-Id": this.orgId,
4279
+ ...deeplakeClientHeader()
3874
4280
  }
3875
4281
  });
3876
4282
  if (resp.ok) {
@@ -3895,29 +4301,84 @@ var DeeplakeApi = class {
3895
4301
  }
3896
4302
  return { tables: [], cacheable: false };
3897
4303
  }
4304
+ /**
4305
+ * Run a `CREATE TABLE` with an extra outer retry budget. The base
4306
+ * `query()` already retries 3 times on fetch errors (~3.5s total), but a
4307
+ * failed CREATE is permanent corruption — every subsequent SELECT against
4308
+ * the missing table fails. Wrapping in an outer loop with longer backoff
4309
+ * (2s, 5s, then 10s) gives us ~17s of reach across transient network
4310
+ * blips before giving up. Failures still propagate; getApi() resets its
4311
+ * cache on init failure (openclaw plugin) so the next call retries the
4312
+ * whole init flow.
4313
+ */
4314
+ async createTableWithRetry(sql, label) {
4315
+ const OUTER_BACKOFFS_MS = [2e3, 5e3, 1e4];
4316
+ let lastErr = null;
4317
+ for (let attempt = 0; attempt <= OUTER_BACKOFFS_MS.length; attempt++) {
4318
+ try {
4319
+ await this.query(sql);
4320
+ return;
4321
+ } catch (err) {
4322
+ lastErr = err;
4323
+ const msg = err instanceof Error ? err.message : String(err);
4324
+ log3(`CREATE TABLE "${label}" attempt ${attempt + 1}/${OUTER_BACKOFFS_MS.length + 1} failed: ${msg}`);
4325
+ if (attempt < OUTER_BACKOFFS_MS.length) {
4326
+ await sleep(OUTER_BACKOFFS_MS[attempt]);
4327
+ }
4328
+ }
4329
+ }
4330
+ throw lastErr;
4331
+ }
3898
4332
  /** Create the memory table if it doesn't already exist. Migrate columns on existing tables. */
3899
4333
  async ensureTable(name) {
3900
- const tbl = name ?? this.tableName;
4334
+ const tbl = sqlIdent(name ?? this.tableName);
3901
4335
  const tables = await this.listTables();
3902
4336
  if (!tables.includes(tbl)) {
3903
4337
  log3(`table "${tbl}" not found, creating`);
3904
- 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`);
4338
+ 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 '', creation_date TEXT NOT NULL DEFAULT '', last_update_date TEXT NOT NULL DEFAULT '') USING deeplake`, tbl);
3905
4339
  log3(`table "${tbl}" created`);
3906
4340
  if (!tables.includes(tbl))
3907
4341
  this._tablesCache = [...tables, tbl];
3908
4342
  }
4343
+ await this.ensureEmbeddingColumn(tbl, SUMMARY_EMBEDDING_COL);
4344
+ await this.ensureColumn(tbl, "agent", "TEXT NOT NULL DEFAULT ''");
3909
4345
  }
3910
4346
  /** Create the sessions table (uses JSONB for message since every row is a JSON event). */
3911
4347
  async ensureSessionsTable(name) {
4348
+ const safe = sqlIdent(name);
4349
+ const tables = await this.listTables();
4350
+ if (!tables.includes(safe)) {
4351
+ log3(`table "${safe}" not found, creating`);
4352
+ 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 '', creation_date TEXT NOT NULL DEFAULT '', last_update_date TEXT NOT NULL DEFAULT '') USING deeplake`, safe);
4353
+ log3(`table "${safe}" created`);
4354
+ if (!tables.includes(safe))
4355
+ this._tablesCache = [...tables, safe];
4356
+ }
4357
+ await this.ensureEmbeddingColumn(safe, MESSAGE_EMBEDDING_COL);
4358
+ await this.ensureColumn(safe, "agent", "TEXT NOT NULL DEFAULT ''");
4359
+ await this.ensureLookupIndex(safe, "path_creation_date", `("path", "creation_date")`);
4360
+ }
4361
+ /**
4362
+ * Create the skills table.
4363
+ *
4364
+ * One row per skill version. Workers INSERT a fresh row on every KEEP /
4365
+ * MERGE rather than UPDATE-ing in place, so the full version history is
4366
+ * recoverable. Uniqueness in the *current* state is by (project_key, name)
4367
+ * — newer rows shadow older ones at read time (ORDER BY version DESC).
4368
+ * This sidesteps the Deeplake UPDATE-coalescing quirk that bit the wiki
4369
+ * worker.
4370
+ */
4371
+ async ensureSkillsTable(name) {
4372
+ const safe = sqlIdent(name);
3912
4373
  const tables = await this.listTables();
3913
- if (!tables.includes(name)) {
3914
- log3(`table "${name}" not found, creating`);
3915
- 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`);
3916
- log3(`table "${name}" created`);
3917
- if (!tables.includes(name))
3918
- this._tablesCache = [...tables, name];
4374
+ if (!tables.includes(safe)) {
4375
+ log3(`table "${safe}" not found, creating`);
4376
+ 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);
4377
+ log3(`table "${safe}" created`);
4378
+ if (!tables.includes(safe))
4379
+ this._tablesCache = [...tables, safe];
3919
4380
  }
3920
- await this.ensureLookupIndex(name, "path_creation_date", `("path", "creation_date")`);
4381
+ await this.ensureLookupIndex(safe, "project_key_name", `("project_key", "name")`);
3921
4382
  }
3922
4383
  };
3923
4384
 
@@ -4097,8 +4558,24 @@ async function runAuthCommand(args) {
4097
4558
  console.log(`Org not found: ${target}`);
4098
4559
  process.exit(1);
4099
4560
  }
4561
+ const prevWs = creds.workspaceId ?? "default";
4562
+ const lcPrev = prevWs.toLowerCase();
4563
+ const wsList = await listWorkspaces(creds.token, apiUrl, match.id);
4564
+ const matchedWs = wsList.find((w) => w.id === prevWs || w.name && w.name.toLowerCase() === lcPrev);
4100
4565
  await switchOrg(match.id, match.name);
4101
4566
  console.log(`Switched to org: ${match.name}`);
4567
+ if (!matchedWs) {
4568
+ if (prevWs !== "default") {
4569
+ await switchWorkspace("default");
4570
+ console.log(`Workspace '${prevWs}' is not in org '${match.name}'. Reset workspace to 'default'.`);
4571
+ if (wsList.length > 0) {
4572
+ console.log(`Available workspaces: ${wsList.map((w) => w.name || w.id).join(", ")}`);
4573
+ }
4574
+ }
4575
+ } else if (matchedWs.id !== prevWs) {
4576
+ await switchWorkspace(matchedWs.id);
4577
+ console.log(`Workspace name '${prevWs}' resolved to id '${matchedWs.id}' in org '${match.name}'.`);
4578
+ }
4102
4579
  } else {
4103
4580
  console.log("Usage: org list | org switch <name-or-id>");
4104
4581
  }
@@ -4110,7 +4587,7 @@ async function runAuthCommand(args) {
4110
4587
  process.exit(1);
4111
4588
  }
4112
4589
  const ws = await listWorkspaces(creds.token, apiUrl, creds.orgId);
4113
- ws.forEach((w) => console.log(`${w.id} ${w.name}`));
4590
+ ws.forEach((w) => console.log(w.name || w.id));
4114
4591
  break;
4115
4592
  }
4116
4593
  case "workspace": {
@@ -4118,14 +4595,34 @@ async function runAuthCommand(args) {
4118
4595
  console.log("Not logged in.");
4119
4596
  process.exit(1);
4120
4597
  }
4121
- const wsId = args[1];
4122
- if (!wsId) {
4123
- console.log("Usage: workspace <id>");
4124
- process.exit(1);
4598
+ const sub = args[1];
4599
+ if (sub === "list") {
4600
+ const wsList = await listWorkspaces(creds.token, apiUrl, creds.orgId);
4601
+ wsList.forEach((w) => console.log(w.name || w.id));
4602
+ break;
4125
4603
  }
4126
- await switchWorkspace(wsId);
4127
- console.log(`Switched to workspace: ${wsId}`);
4128
- break;
4604
+ if (sub === "switch") {
4605
+ const target = args[2];
4606
+ if (!target) {
4607
+ console.log("Usage: workspace switch <name-or-id>");
4608
+ process.exit(1);
4609
+ }
4610
+ const wsList = await listWorkspaces(creds.token, apiUrl, creds.orgId);
4611
+ const lcTarget = target.toLowerCase();
4612
+ const match = wsList.find((w) => w.id === target || w.name && w.name.toLowerCase() === lcTarget);
4613
+ if (!match) {
4614
+ console.log(`Workspace not found: ${target}`);
4615
+ if (wsList.length > 0) {
4616
+ console.log(`Available workspaces: ${wsList.map((w) => w.name || w.id).join(", ")}`);
4617
+ }
4618
+ process.exit(1);
4619
+ }
4620
+ await switchWorkspace(match.id);
4621
+ console.log(`Switched to workspace: ${match.name || match.id}`);
4622
+ break;
4623
+ }
4624
+ console.log("Usage: workspace list | workspace switch <name-or-id>");
4625
+ process.exit(1);
4129
4626
  }
4130
4627
  case "invite": {
4131
4628
  if (!creds) {
@@ -4212,6 +4709,699 @@ if (process.argv[1] && process.argv[1].endsWith("auth-login.js")) {
4212
4709
  });
4213
4710
  }
4214
4711
 
4712
+ // dist/src/commands/skilify.js
4713
+ import { readdirSync as readdirSync3, existsSync as existsSync15, readFileSync as readFileSync12, mkdirSync as mkdirSync7, renameSync as renameSync2 } from "node:fs";
4714
+ import { homedir as homedir8 } from "node:os";
4715
+ import { dirname as dirname2, join as join18 } from "node:path";
4716
+
4717
+ // dist/src/skilify/scope-config.js
4718
+ import { existsSync as existsSync12, mkdirSync as mkdirSync4, readFileSync as readFileSync9, writeFileSync as writeFileSync6 } from "node:fs";
4719
+ import { homedir as homedir5 } from "node:os";
4720
+ import { join as join15 } from "node:path";
4721
+ var STATE_DIR = join15(homedir5(), ".deeplake", "state", "skilify");
4722
+ var CONFIG_PATH2 = join15(STATE_DIR, "config.json");
4723
+ var DEFAULT = { scope: "me", team: [], install: "project" };
4724
+ function loadScopeConfig() {
4725
+ if (!existsSync12(CONFIG_PATH2))
4726
+ return DEFAULT;
4727
+ try {
4728
+ const raw = JSON.parse(readFileSync9(CONFIG_PATH2, "utf-8"));
4729
+ const scope = raw.scope === "team" || raw.scope === "org" ? raw.scope : "me";
4730
+ const team = Array.isArray(raw.team) ? raw.team.filter((s) => typeof s === "string") : [];
4731
+ const install = raw.install === "global" ? "global" : "project";
4732
+ return { scope, team, install };
4733
+ } catch {
4734
+ return DEFAULT;
4735
+ }
4736
+ }
4737
+ function saveScopeConfig(cfg) {
4738
+ mkdirSync4(STATE_DIR, { recursive: true });
4739
+ writeFileSync6(CONFIG_PATH2, JSON.stringify(cfg, null, 2));
4740
+ }
4741
+
4742
+ // dist/src/skilify/pull.js
4743
+ import { existsSync as existsSync14, readFileSync as readFileSync11, writeFileSync as writeFileSync8, mkdirSync as mkdirSync6, renameSync } from "node:fs";
4744
+ import { homedir as homedir7 } from "node:os";
4745
+ import { join as join17 } from "node:path";
4746
+
4747
+ // dist/src/skilify/skill-writer.js
4748
+ import { existsSync as existsSync13, mkdirSync as mkdirSync5, readFileSync as readFileSync10, readdirSync as readdirSync2, statSync as statSync2, writeFileSync as writeFileSync7 } from "node:fs";
4749
+ import { homedir as homedir6 } from "node:os";
4750
+ import { join as join16 } from "node:path";
4751
+ function assertValidSkillName(name) {
4752
+ if (typeof name !== "string" || name.length === 0) {
4753
+ throw new Error(`invalid skill name: empty or non-string`);
4754
+ }
4755
+ if (name.length > 100) {
4756
+ throw new Error(`invalid skill name: too long (${name.length} chars)`);
4757
+ }
4758
+ if (name.includes("/") || name.includes("\\") || name.includes("..")) {
4759
+ throw new Error(`invalid skill name: contains path separator or '..': ${name}`);
4760
+ }
4761
+ if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(name)) {
4762
+ throw new Error(`invalid skill name: must be kebab-case (lowercase a-z, 0-9, hyphen): ${name}`);
4763
+ }
4764
+ }
4765
+ function parseFrontmatter(text) {
4766
+ if (!text.startsWith("---\n") && !text.startsWith("---\r\n"))
4767
+ return null;
4768
+ const end = text.indexOf("\n---", 4);
4769
+ if (end < 0)
4770
+ return null;
4771
+ const head = text.slice(4, end).trim();
4772
+ const body = text.slice(end + 4).replace(/^\r?\n/, "");
4773
+ const fm = { source_sessions: [] };
4774
+ let mode = "kv";
4775
+ for (const raw of head.split(/\r?\n/)) {
4776
+ if (mode === "sources") {
4777
+ const m2 = raw.match(/^\s+-\s+(.+)$/);
4778
+ if (m2) {
4779
+ fm.source_sessions.push(m2[1].trim());
4780
+ continue;
4781
+ }
4782
+ mode = "kv";
4783
+ }
4784
+ if (raw.startsWith("source_sessions:")) {
4785
+ mode = "sources";
4786
+ continue;
4787
+ }
4788
+ const m = raw.match(/^([a-zA-Z_]+):\s*(.*)$/);
4789
+ if (!m)
4790
+ continue;
4791
+ const [, k, v] = m;
4792
+ let val = v;
4793
+ if (v.startsWith('"') && v.endsWith('"')) {
4794
+ try {
4795
+ val = JSON.parse(v);
4796
+ } catch {
4797
+ }
4798
+ } else if (k === "version") {
4799
+ const n = parseInt(v, 10);
4800
+ if (Number.isFinite(n))
4801
+ val = n;
4802
+ }
4803
+ fm[k] = val;
4804
+ }
4805
+ return { fm, body };
4806
+ }
4807
+
4808
+ // dist/src/skilify/pull.js
4809
+ function esc(s) {
4810
+ return s.replace(/\\/g, "\\\\").replace(/'/g, "''").replace(/[\x01-\x08\x0b\x0c\x0e-\x1f\x7f]/g, "");
4811
+ }
4812
+ function buildPullSql(args) {
4813
+ const where = [];
4814
+ if (args.users.length > 0) {
4815
+ const list = args.users.map((u) => `'${esc(u)}'`).join(", ");
4816
+ where.push(`author IN (${list})`);
4817
+ }
4818
+ if (args.skillName) {
4819
+ where.push(`name = '${esc(args.skillName)}'`);
4820
+ }
4821
+ const whereClause = where.length > 0 ? ` WHERE ${where.join(" AND ")}` : "";
4822
+ return `SELECT name, project, project_key, body, version, source_agent, scope, author, description, trigger_text, source_sessions, install, created_at, updated_at FROM "${args.tableName}"${whereClause} ORDER BY project_key ASC, name ASC, version DESC`;
4823
+ }
4824
+ function isMissingTableError(message) {
4825
+ if (!message)
4826
+ return false;
4827
+ return /Table does not exist|relation .* does not exist|no such table/i.test(message);
4828
+ }
4829
+ function resolvePullDestination(install, cwd) {
4830
+ if (install === "global")
4831
+ return join17(homedir7(), ".claude", "skills");
4832
+ if (!cwd)
4833
+ throw new Error("install=project requires a cwd");
4834
+ return join17(cwd, ".claude", "skills");
4835
+ }
4836
+ function selectLatestPerName(rows) {
4837
+ const seen = /* @__PURE__ */ new Set();
4838
+ const out = [];
4839
+ for (const r of rows) {
4840
+ const name = String(r.name ?? "");
4841
+ const projectKey = String(r.project_key ?? "");
4842
+ if (!name)
4843
+ continue;
4844
+ const key = `${projectKey}\0${name}`;
4845
+ if (seen.has(key))
4846
+ continue;
4847
+ seen.add(key);
4848
+ out.push(r);
4849
+ }
4850
+ return out;
4851
+ }
4852
+ function renderSkillFile(row) {
4853
+ const sources = parseSourceSessions(row.source_sessions);
4854
+ const fm = {
4855
+ name: String(row.name ?? ""),
4856
+ description: String(row.description ?? ""),
4857
+ trigger: typeof row.trigger_text === "string" && row.trigger_text.length > 0 ? String(row.trigger_text) : void 0,
4858
+ source_sessions: sources,
4859
+ version: Number(row.version ?? 1),
4860
+ created_by_agent: String(row.source_agent ?? "unknown"),
4861
+ created_at: String(row.created_at ?? (/* @__PURE__ */ new Date()).toISOString()),
4862
+ updated_at: String(row.updated_at ?? (/* @__PURE__ */ new Date()).toISOString())
4863
+ };
4864
+ const body = String(row.body ?? "").trim();
4865
+ return `${renderFrontmatter(fm)}
4866
+
4867
+ ${body}
4868
+ `;
4869
+ }
4870
+ function parseSourceSessions(v) {
4871
+ if (Array.isArray(v))
4872
+ return v.map(String);
4873
+ if (typeof v === "string") {
4874
+ try {
4875
+ const parsed = JSON.parse(v);
4876
+ if (Array.isArray(parsed))
4877
+ return parsed.map(String);
4878
+ } catch {
4879
+ }
4880
+ }
4881
+ return [];
4882
+ }
4883
+ function renderFrontmatter(fm) {
4884
+ const lines = ["---"];
4885
+ lines.push(`name: ${fm.name}`);
4886
+ lines.push(`description: ${JSON.stringify(fm.description)}`);
4887
+ if (fm.trigger)
4888
+ lines.push(`trigger: ${JSON.stringify(fm.trigger)}`);
4889
+ lines.push(`source_sessions:`);
4890
+ for (const s of fm.source_sessions)
4891
+ lines.push(` - ${s}`);
4892
+ lines.push(`version: ${fm.version}`);
4893
+ lines.push(`created_by_agent: ${fm.created_by_agent}`);
4894
+ lines.push(`created_at: ${fm.created_at}`);
4895
+ lines.push(`updated_at: ${fm.updated_at}`);
4896
+ lines.push("---");
4897
+ return lines.join("\n");
4898
+ }
4899
+ function readLocalVersion(path) {
4900
+ if (!existsSync14(path))
4901
+ return null;
4902
+ try {
4903
+ const text = readFileSync11(path, "utf-8");
4904
+ const parsed = parseFrontmatter(text);
4905
+ if (!parsed)
4906
+ return null;
4907
+ const v = parsed.fm.version;
4908
+ return typeof v === "number" ? v : null;
4909
+ } catch {
4910
+ return null;
4911
+ }
4912
+ }
4913
+ function decideAction(args) {
4914
+ const shouldWrite = args.localVersion === null || args.remoteVersion > args.localVersion || args.force;
4915
+ if (!shouldWrite)
4916
+ return "skipped";
4917
+ return args.dryRun ? "dryrun" : "wrote";
4918
+ }
4919
+ async function runPull(opts) {
4920
+ const sql = buildPullSql({
4921
+ tableName: opts.tableName,
4922
+ users: opts.users,
4923
+ skillName: opts.skillName
4924
+ });
4925
+ let rows = [];
4926
+ try {
4927
+ rows = await opts.query(sql);
4928
+ } catch (e) {
4929
+ if (isMissingTableError(e?.message))
4930
+ rows = [];
4931
+ else
4932
+ throw e;
4933
+ }
4934
+ const latest = selectLatestPerName(rows);
4935
+ const root = resolvePullDestination(opts.install, opts.cwd);
4936
+ const summary = { scanned: latest.length, wrote: 0, skipped: 0, dryrun: 0, entries: [] };
4937
+ for (const row of latest) {
4938
+ const name = String(row.name ?? "");
4939
+ if (!name)
4940
+ continue;
4941
+ try {
4942
+ assertValidSkillName(name);
4943
+ } catch (e) {
4944
+ summary.entries.push({
4945
+ name,
4946
+ remoteVersion: Number(row.version ?? 1),
4947
+ localVersion: null,
4948
+ action: "skipped",
4949
+ destination: "(invalid name \u2014 skipped)",
4950
+ author: String(row.author ?? ""),
4951
+ sourceAgent: String(row.source_agent ?? "")
4952
+ });
4953
+ summary.skipped++;
4954
+ continue;
4955
+ }
4956
+ const projectKey = String(row.project_key ?? "");
4957
+ const skillDir = projectKey ? join17(root, projectKey, name) : join17(root, name);
4958
+ const skillFile = join17(skillDir, "SKILL.md");
4959
+ const remoteVersion = Number(row.version ?? 1);
4960
+ const localVersion = readLocalVersion(skillFile);
4961
+ const action = decideAction({
4962
+ remoteVersion,
4963
+ localVersion,
4964
+ force: opts.force ?? false,
4965
+ dryRun: opts.dryRun ?? false
4966
+ });
4967
+ if (action === "wrote") {
4968
+ mkdirSync6(skillDir, { recursive: true });
4969
+ if (existsSync14(skillFile)) {
4970
+ try {
4971
+ renameSync(skillFile, `${skillFile}.bak`);
4972
+ } catch {
4973
+ }
4974
+ }
4975
+ writeFileSync8(skillFile, renderSkillFile(row));
4976
+ }
4977
+ summary.entries.push({
4978
+ name,
4979
+ remoteVersion,
4980
+ localVersion,
4981
+ action,
4982
+ destination: skillFile,
4983
+ author: String(row.author ?? ""),
4984
+ sourceAgent: String(row.source_agent ?? "")
4985
+ });
4986
+ if (action === "wrote")
4987
+ summary.wrote++;
4988
+ else if (action === "dryrun")
4989
+ summary.dryrun++;
4990
+ else
4991
+ summary.skipped++;
4992
+ }
4993
+ return summary;
4994
+ }
4995
+
4996
+ // dist/src/commands/skilify.js
4997
+ var STATE_DIR2 = join18(homedir8(), ".deeplake", "state", "skilify");
4998
+ function showStatus() {
4999
+ const cfg = loadScopeConfig();
5000
+ console.log(`scope: ${cfg.scope}`);
5001
+ console.log(`team: ${cfg.team.length === 0 ? "(empty)" : cfg.team.join(", ")}`);
5002
+ console.log(`install: ${cfg.install} (${cfg.install === "global" ? "~/.claude/skills/" : "<project>/.claude/skills/"})`);
5003
+ if (!existsSync15(STATE_DIR2)) {
5004
+ console.log(`state: (no projects tracked yet)`);
5005
+ return;
5006
+ }
5007
+ const files = readdirSync3(STATE_DIR2).filter((f) => f.endsWith(".json") && f !== "config.json");
5008
+ if (files.length === 0) {
5009
+ console.log(`state: (no projects tracked yet)`);
5010
+ return;
5011
+ }
5012
+ console.log(`state: ${files.length} project(s) tracked`);
5013
+ for (const f of files) {
5014
+ try {
5015
+ const s = JSON.parse(readFileSync12(join18(STATE_DIR2, f), "utf-8"));
5016
+ const skills = s.skillsGenerated.length === 0 ? "none" : s.skillsGenerated.join(", ");
5017
+ console.log(` - ${s.project} (counter=${s.counter}, last=${s.lastDate ?? "never"}, skills=${skills})`);
5018
+ } catch {
5019
+ }
5020
+ }
5021
+ }
5022
+ function setScope(scope) {
5023
+ if (scope !== "me" && scope !== "team" && scope !== "org") {
5024
+ console.error(`Invalid scope '${scope}'. Use one of: me, team, org`);
5025
+ process.exit(1);
5026
+ }
5027
+ const cfg = loadScopeConfig();
5028
+ saveScopeConfig({ ...cfg, scope });
5029
+ console.log(`Scope set to '${scope}'.`);
5030
+ if (scope === "team" && cfg.team.length === 0) {
5031
+ console.log(`Note: team list is empty. Use 'hivemind skilify team add <username>' to populate it.`);
5032
+ }
5033
+ }
5034
+ function setInstall(loc) {
5035
+ if (loc !== "project" && loc !== "global") {
5036
+ console.error(`Invalid install location '${loc}'. Use one of: project, global`);
5037
+ process.exit(1);
5038
+ }
5039
+ const cfg = loadScopeConfig();
5040
+ saveScopeConfig({ ...cfg, install: loc });
5041
+ const path = loc === "global" ? join18(homedir8(), ".claude", "skills") : "<cwd>/.claude/skills";
5042
+ console.log(`Install location set to '${loc}'. New skills will be written to ${path}/<name>/SKILL.md.`);
5043
+ }
5044
+ function promoteSkill(name, cwd) {
5045
+ if (!name) {
5046
+ console.error("Usage: hivemind skilify promote <skill-name>");
5047
+ process.exit(1);
5048
+ }
5049
+ const projectPath = join18(cwd, ".claude", "skills", name);
5050
+ const globalPath = join18(homedir8(), ".claude", "skills", name);
5051
+ if (!existsSync15(join18(projectPath, "SKILL.md"))) {
5052
+ console.error(`Skill '${name}' not found at ${projectPath}/SKILL.md`);
5053
+ process.exit(1);
5054
+ }
5055
+ if (existsSync15(join18(globalPath, "SKILL.md"))) {
5056
+ console.error(`Skill '${name}' already exists at ${globalPath}/SKILL.md \u2014 refusing to overwrite. Remove it first or rename the project skill.`);
5057
+ process.exit(1);
5058
+ }
5059
+ mkdirSync7(dirname2(globalPath), { recursive: true });
5060
+ renameSync2(projectPath, globalPath);
5061
+ console.log(`Promoted '${name}' from ${projectPath} \u2192 ${globalPath}.`);
5062
+ }
5063
+ function teamAdd(name) {
5064
+ if (!name) {
5065
+ console.error("Usage: hivemind skilify team add <username>");
5066
+ process.exit(1);
5067
+ }
5068
+ const cfg = loadScopeConfig();
5069
+ if (cfg.team.includes(name)) {
5070
+ console.log(`'${name}' is already in the team list.`);
5071
+ return;
5072
+ }
5073
+ const next = [...cfg.team, name].sort();
5074
+ saveScopeConfig({ ...cfg, team: next });
5075
+ console.log(`Added '${name}' to team. Team is now: ${next.join(", ")}`);
5076
+ }
5077
+ function teamRemove(name) {
5078
+ if (!name) {
5079
+ console.error("Usage: hivemind skilify team remove <username>");
5080
+ process.exit(1);
5081
+ }
5082
+ const cfg = loadScopeConfig();
5083
+ if (!cfg.team.includes(name)) {
5084
+ console.log(`'${name}' is not in the team list.`);
5085
+ return;
5086
+ }
5087
+ const next = cfg.team.filter((n) => n !== name);
5088
+ saveScopeConfig({ ...cfg, team: next });
5089
+ console.log(`Removed '${name}' from team. Team is now: ${next.length === 0 ? "(empty)" : next.join(", ")}`);
5090
+ }
5091
+ function teamList() {
5092
+ const cfg = loadScopeConfig();
5093
+ if (cfg.team.length === 0) {
5094
+ console.log(`(team list is empty)`);
5095
+ return;
5096
+ }
5097
+ for (const n of cfg.team)
5098
+ console.log(n);
5099
+ }
5100
+ function usage() {
5101
+ console.log("Usage:");
5102
+ console.log(" hivemind skilify show current scope, team, install, and per-project state");
5103
+ console.log(" hivemind skilify scope <me|team|org> set the mining scope");
5104
+ console.log(" hivemind skilify install <project|global> set where new skills are written");
5105
+ console.log(" hivemind skilify promote <skill-name> move a project skill to the global location");
5106
+ console.log(" hivemind skilify team add <username> add a username to the team list");
5107
+ console.log(" hivemind skilify team remove <username> remove a username from the team list");
5108
+ console.log(" hivemind skilify team list list current team members");
5109
+ console.log(" hivemind skilify pull [skill-name] [opts] fetch skills from Deeplake to local FS");
5110
+ console.log(" Options for pull:");
5111
+ console.log(" --to <project|global> destination (default: global)");
5112
+ console.log(" --user <name> only skills authored by this user");
5113
+ console.log(" --users <a,b,c> only skills authored by these users");
5114
+ console.log(" --all-users all authors (default \u2014 equivalent to no filter)");
5115
+ console.log(" --dry-run show what would be written, don't touch disk");
5116
+ console.log(" --force overwrite even when local version >= remote");
5117
+ console.log(" hivemind skilify status show per-project state");
5118
+ }
5119
+ function takeFlagValue(args, flag) {
5120
+ const idx = args.indexOf(flag);
5121
+ if (idx < 0)
5122
+ return null;
5123
+ const value = args[idx + 1];
5124
+ if (value === void 0 || value.startsWith("--")) {
5125
+ console.error(`${flag} requires a value`);
5126
+ process.exit(1);
5127
+ }
5128
+ args.splice(idx, 2);
5129
+ return value;
5130
+ }
5131
+ function takeBooleanFlag(args, flag) {
5132
+ const idx = args.indexOf(flag);
5133
+ if (idx < 0)
5134
+ return false;
5135
+ args.splice(idx, 1);
5136
+ return true;
5137
+ }
5138
+ async function pullSkills(args) {
5139
+ const work = [...args];
5140
+ const toRaw = takeFlagValue(work, "--to") ?? "global";
5141
+ const userOne = takeFlagValue(work, "--user");
5142
+ const usersMany = takeFlagValue(work, "--users");
5143
+ const allUsers = takeBooleanFlag(work, "--all-users");
5144
+ const dryRun = takeBooleanFlag(work, "--dry-run");
5145
+ const force = takeBooleanFlag(work, "--force");
5146
+ const skillName = work[0];
5147
+ if (toRaw !== "project" && toRaw !== "global") {
5148
+ console.error(`Invalid --to '${toRaw}'. Use 'project' or 'global'.`);
5149
+ process.exit(1);
5150
+ }
5151
+ let users = [];
5152
+ if (allUsers)
5153
+ users = [];
5154
+ else if (userOne)
5155
+ users = [userOne];
5156
+ else if (usersMany)
5157
+ users = usersMany.split(",").map((s) => s.trim()).filter(Boolean);
5158
+ const config = loadConfig();
5159
+ if (!config) {
5160
+ console.error("Not logged in. Run: hivemind login");
5161
+ process.exit(1);
5162
+ }
5163
+ const api = new DeeplakeApi(config.token, config.apiUrl, config.orgId, config.workspaceId, config.skillsTableName);
5164
+ const query = (sql) => api.query(sql);
5165
+ let summary;
5166
+ try {
5167
+ summary = await runPull({
5168
+ query,
5169
+ tableName: config.skillsTableName,
5170
+ install: toRaw,
5171
+ cwd: toRaw === "project" ? process.cwd() : void 0,
5172
+ users,
5173
+ skillName,
5174
+ dryRun,
5175
+ force
5176
+ });
5177
+ } catch (e) {
5178
+ console.error(`pull failed: ${e?.message ?? e}`);
5179
+ process.exit(1);
5180
+ }
5181
+ const dest = toRaw === "global" ? join18(homedir8(), ".claude", "skills") : `${process.cwd()}/.claude/skills`;
5182
+ const filterDesc = users.length === 0 ? "all users" : users.join(", ");
5183
+ console.log(`Destination: ${dest}`);
5184
+ console.log(`Filter: ${filterDesc}${skillName ? ` \xB7 skill='${skillName}'` : ""}${dryRun ? " \xB7 dry-run" : ""}${force ? " \xB7 force" : ""}`);
5185
+ console.log(`Scanned ${summary.scanned} remote skill(s).`);
5186
+ for (const e of summary.entries) {
5187
+ const tag = e.action === "wrote" ? "\u2713 wrote" : e.action === "dryrun" ? "\u2192 would write" : "\xB7 skipped";
5188
+ const ver = e.localVersion === null ? `v${e.remoteVersion} (new)` : `v${e.localVersion} \u2192 v${e.remoteVersion}`;
5189
+ console.log(` ${tag.padEnd(15)} ${e.name.padEnd(40)} ${ver.padEnd(20)} (${e.author}/${e.sourceAgent})`);
5190
+ }
5191
+ console.log(`Result: ${summary.wrote} written, ${summary.dryrun} dry-run, ${summary.skipped} skipped.`);
5192
+ }
5193
+ function runSkilifyCommand(args) {
5194
+ const sub = args[0];
5195
+ if (!sub || sub === "status") {
5196
+ showStatus();
5197
+ return;
5198
+ }
5199
+ if (sub === "scope") {
5200
+ setScope(args[1] ?? "");
5201
+ return;
5202
+ }
5203
+ if (sub === "install") {
5204
+ setInstall(args[1] ?? "");
5205
+ return;
5206
+ }
5207
+ if (sub === "promote") {
5208
+ promoteSkill(args[1] ?? "", process.cwd());
5209
+ return;
5210
+ }
5211
+ if (sub === "pull") {
5212
+ pullSkills(args.slice(1)).catch((e) => {
5213
+ console.error(`pull error: ${e?.message ?? e}`);
5214
+ process.exit(1);
5215
+ });
5216
+ return;
5217
+ }
5218
+ if (sub === "team") {
5219
+ const action = args[1];
5220
+ if (action === "add") {
5221
+ teamAdd(args[2] ?? "");
5222
+ return;
5223
+ }
5224
+ if (action === "remove") {
5225
+ teamRemove(args[2] ?? "");
5226
+ return;
5227
+ }
5228
+ if (action === "list") {
5229
+ teamList();
5230
+ return;
5231
+ }
5232
+ console.error("Usage: hivemind skilify team <add|remove|list> [name]");
5233
+ process.exit(1);
5234
+ }
5235
+ if (sub === "--help" || sub === "-h" || sub === "help") {
5236
+ usage();
5237
+ return;
5238
+ }
5239
+ console.error(`Unknown skilify subcommand: ${sub}`);
5240
+ usage();
5241
+ process.exit(1);
5242
+ }
5243
+ if (process.argv[1] && process.argv[1].endsWith("skilify.js")) {
5244
+ runSkilifyCommand(process.argv.slice(2));
5245
+ }
5246
+
5247
+ // dist/src/cli/update.js
5248
+ import { execFileSync as execFileSync4 } from "node:child_process";
5249
+ import { existsSync as existsSync16, readFileSync as readFileSync14, realpathSync } from "node:fs";
5250
+ import { dirname as dirname4, sep } from "node:path";
5251
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
5252
+
5253
+ // dist/src/utils/version-check.js
5254
+ import { readFileSync as readFileSync13 } from "node:fs";
5255
+ import { dirname as dirname3, join as join19 } from "node:path";
5256
+ function isNewer(latest, current) {
5257
+ const parse = (v) => v.split(".").map(Number);
5258
+ const [la, lb, lc] = parse(latest);
5259
+ const [ca, cb, cc] = parse(current);
5260
+ return la > ca || la === ca && lb > cb || la === ca && lb === cb && lc > cc;
5261
+ }
5262
+
5263
+ // dist/src/cli/update.js
5264
+ var NPM_REGISTRY_URL = "https://registry.npmjs.org/@deeplake/hivemind/latest";
5265
+ var PKG_NAME = "@deeplake/hivemind";
5266
+ function detectInstallKind(argv1) {
5267
+ const realArgv1 = (() => {
5268
+ try {
5269
+ return realpathSync(argv1 ?? process.argv[1] ?? fileURLToPath2(import.meta.url));
5270
+ } catch {
5271
+ return argv1 ?? process.argv[1] ?? fileURLToPath2(import.meta.url);
5272
+ }
5273
+ })();
5274
+ let dir = dirname4(realArgv1);
5275
+ let installDir = null;
5276
+ for (let i = 0; i < 10; i++) {
5277
+ const pkgPath = `${dir}${sep}package.json`;
5278
+ try {
5279
+ const pkg = JSON.parse(readFileSync14(pkgPath, "utf-8"));
5280
+ if (pkg.name === PKG_NAME || pkg.name === "hivemind") {
5281
+ installDir = dir;
5282
+ break;
5283
+ }
5284
+ } catch {
5285
+ }
5286
+ const parent = dirname4(dir);
5287
+ if (parent === dir)
5288
+ break;
5289
+ dir = parent;
5290
+ }
5291
+ installDir ??= dirname4(realArgv1);
5292
+ if (realArgv1.includes(`${sep}_npx${sep}`) || realArgv1.includes(`${sep}.npx${sep}`)) {
5293
+ return { kind: "npx", installDir };
5294
+ }
5295
+ if (realArgv1.includes(`${sep}node_modules${sep}@deeplake${sep}hivemind`) || realArgv1.includes(`${sep}node_modules${sep}hivemind`)) {
5296
+ return { kind: "npm-global", installDir };
5297
+ }
5298
+ let gitDir = installDir;
5299
+ for (let i = 0; i < 6; i++) {
5300
+ if (existsSync16(`${gitDir}${sep}.git`)) {
5301
+ return { kind: "local-dev", installDir };
5302
+ }
5303
+ const parent = dirname4(gitDir);
5304
+ if (parent === gitDir)
5305
+ break;
5306
+ gitDir = parent;
5307
+ }
5308
+ return { kind: "unknown", installDir };
5309
+ }
5310
+ async function getLatestNpmVersion(timeoutMs = 5e3) {
5311
+ try {
5312
+ const res = await fetch(NPM_REGISTRY_URL, { signal: AbortSignal.timeout(timeoutMs) });
5313
+ if (!res.ok)
5314
+ return null;
5315
+ const meta = await res.json();
5316
+ return meta.version ?? null;
5317
+ } catch {
5318
+ return null;
5319
+ }
5320
+ }
5321
+ var defaultSpawn = (cmd, args) => {
5322
+ execFileSync4(cmd, args, { stdio: "inherit" });
5323
+ };
5324
+ async function runUpdate(opts = {}) {
5325
+ const current = opts.currentVersionOverride ?? getVersion();
5326
+ const latest = opts.latestVersionOverride !== void 0 ? opts.latestVersionOverride : await getLatestNpmVersion();
5327
+ if (!latest) {
5328
+ warn(`Could not reach npm registry to check for updates.`);
5329
+ warn(`Current version: ${current}`);
5330
+ return 1;
5331
+ }
5332
+ if (!isNewer(latest, current)) {
5333
+ log(`hivemind ${current} is up to date (npm latest: ${latest}).`);
5334
+ return 0;
5335
+ }
5336
+ log(`Update available: ${current} \u2192 ${latest}`);
5337
+ const detected = opts.installKindOverride ?? detectInstallKind();
5338
+ const spawn = opts.spawn ?? defaultSpawn;
5339
+ switch (detected.kind) {
5340
+ case "npm-global": {
5341
+ if (opts.dryRun) {
5342
+ log(`(dry-run) Would run: npm install -g ${PKG_NAME}@latest`);
5343
+ log(`(dry-run) Would re-run: hivemind install --skip-auth`);
5344
+ return 0;
5345
+ }
5346
+ log(`Upgrading via npm\u2026`);
5347
+ try {
5348
+ spawn("npm", ["install", "-g", `${PKG_NAME}@latest`]);
5349
+ } catch (e) {
5350
+ warn(`npm install failed: ${e.message}`);
5351
+ warn(`Try running it manually: npm install -g ${PKG_NAME}@latest`);
5352
+ return 1;
5353
+ }
5354
+ log(``);
5355
+ log(`Refreshing agent bundles\u2026`);
5356
+ try {
5357
+ spawn("hivemind", ["install", "--skip-auth"]);
5358
+ } catch (e) {
5359
+ warn(`Agent refresh failed: ${e.message}`);
5360
+ warn(`Run manually: hivemind install`);
5361
+ return 1;
5362
+ }
5363
+ log(``);
5364
+ log(`Updated to ${latest}.`);
5365
+ return 0;
5366
+ }
5367
+ case "npx": {
5368
+ if (opts.dryRun) {
5369
+ log(`(dry-run) Would print npx-pin instructions (no persistent install to upgrade).`);
5370
+ return 0;
5371
+ }
5372
+ log(`You ran hivemind via npx, which does not have a persistent global install.`);
5373
+ log(`To use the new version, re-run with the explicit version pin:`);
5374
+ log(``);
5375
+ log(` npx ${PKG_NAME}@${latest} install`);
5376
+ log(``);
5377
+ log(`Or install globally so future updates are one command:`);
5378
+ log(``);
5379
+ log(` npm install -g ${PKG_NAME}@latest`);
5380
+ return 0;
5381
+ }
5382
+ case "local-dev": {
5383
+ if (opts.dryRun) {
5384
+ log(`(dry-run) Would refuse: running from a local dev checkout (${detected.installDir}).`);
5385
+ return 0;
5386
+ }
5387
+ warn(`hivemind is running from a local development checkout (${detected.installDir}).`);
5388
+ warn(`Update via your dev workflow (git pull + npm install + npm run build),`);
5389
+ warn(`not via 'hivemind update'.`);
5390
+ return 1;
5391
+ }
5392
+ case "unknown":
5393
+ default: {
5394
+ if (opts.dryRun) {
5395
+ log(`(dry-run) Would refuse: install kind unknown (${detected.installDir}).`);
5396
+ return 0;
5397
+ }
5398
+ warn(`Could not determine how hivemind was installed (path: ${detected.installDir}).`);
5399
+ warn(`Update manually: npm install -g ${PKG_NAME}@latest`);
5400
+ return 1;
5401
+ }
5402
+ }
5403
+ }
5404
+
4215
5405
  // dist/src/cli/index.js
4216
5406
  var AUTH_SUBCOMMANDS = /* @__PURE__ */ new Set([
4217
5407
  "whoami",
@@ -4229,10 +5419,14 @@ var USAGE = `
4229
5419
  hivemind \u2014 one brain for every agent on your team
4230
5420
 
4231
5421
  Usage:
4232
- hivemind install [--only <platforms>] [--skip-auth]
5422
+ hivemind install [--only <platforms>] [--skip-auth]
4233
5423
  Auto-detect assistants on this machine and install hivemind into each.
4234
5424
  --only takes a comma-separated list: ${allPlatformIds().join(",")}
4235
5425
 
5426
+ hivemind uninstall [--only <platforms>]
5427
+ Auto-detect installed assistants and remove hivemind from each.
5428
+ --only takes the same list to scope the removal.
5429
+
4236
5430
  hivemind claude install | uninstall
4237
5431
  hivemind codex install | uninstall
4238
5432
  hivemind claw install | uninstall
@@ -4243,6 +5437,34 @@ Usage:
4243
5437
 
4244
5438
  hivemind login Run device-flow login (open browser).
4245
5439
  hivemind status Show which assistants are wired up.
5440
+ hivemind update [--dry-run]
5441
+ Check npm for a newer @deeplake/hivemind, upgrade the CLI, and refresh
5442
+ every detected agent bundle. Single command for all agents.
5443
+
5444
+ Semantic search (embeddings):
5445
+ hivemind embeddings install Download @huggingface/transformers
5446
+ once (~600 MB) into a shared dir
5447
+ and symlink every detected agent
5448
+ plugin to it. Idempotent.
5449
+ hivemind embeddings uninstall [--prune] Remove the per-agent symlinks.
5450
+ --prune also deletes the shared dir.
5451
+ hivemind embeddings status Show shared-deps + per-agent state.
5452
+
5453
+ Add --with-embeddings to "hivemind install" (or "hivemind <agent> install")
5454
+ to run "embeddings install" automatically after installing the agent(s).
5455
+
5456
+ Skill management (mine + share reusable Claude skills across the org):
5457
+ hivemind skilify Show scope, team, install, and per-project state.
5458
+ hivemind skilify pull [skill-name] Sync skills from the org table to local FS.
5459
+ Options: --user <email>, --users a,b,c,
5460
+ --all-users, --to <project|global>,
5461
+ --dry-run, --force.
5462
+ hivemind skilify scope <me|team|org> Set the sharing scope for newly mined skills.
5463
+ hivemind skilify install <project|global> Set where new skills are written.
5464
+ hivemind skilify promote <name> Move a project skill to the global location.
5465
+ hivemind skilify team add <username> Add a username to the team list.
5466
+ hivemind skilify team remove <username> Remove a username from the team list.
5467
+ hivemind skilify team list List current team members.
4246
5468
 
4247
5469
  Account / org / workspace:
4248
5470
  hivemind whoami Show current user, org, workspace.
@@ -4250,7 +5472,8 @@ Account / org / workspace:
4250
5472
  hivemind org list List organizations.
4251
5473
  hivemind org switch <name-or-id> Switch active organization.
4252
5474
  hivemind workspaces List workspaces in current org.
4253
- hivemind workspace <id> Switch active workspace.
5475
+ hivemind workspace list List workspaces (alias of 'workspaces').
5476
+ hivemind workspace switch <name-or-id> Switch active workspace.
4254
5477
  hivemind members List org members.
4255
5478
  hivemind invite <email> <ADMIN|WRITE|READ> Invite a teammate.
4256
5479
  hivemind remove <user-id> Remove a member.
@@ -4284,6 +5507,7 @@ function hasFlag(args, flag) {
4284
5507
  async function runInstallAll(args) {
4285
5508
  const only = parseOnly(args);
4286
5509
  const skipAuth = hasFlag(args, "--skip-auth");
5510
+ const withEmbeddings = hasFlag(args, "--with-embeddings");
4287
5511
  const targets = only ?? detectPlatforms().map((p) => p.id);
4288
5512
  if (targets.length === 0) {
4289
5513
  log("No supported assistants detected.");
@@ -4302,6 +5526,10 @@ async function runInstallAll(args) {
4302
5526
  }
4303
5527
  for (const id of targets)
4304
5528
  runSingleInstall(id);
5529
+ if (withEmbeddings) {
5530
+ log("");
5531
+ enableEmbeddings();
5532
+ }
4305
5533
  await maybeShowOrgChoice();
4306
5534
  log("");
4307
5535
  log("Done. Restart each assistant to activate hooks.");
@@ -4383,6 +5611,31 @@ async function main() {
4383
5611
  runStatus();
4384
5612
  return;
4385
5613
  }
5614
+ if (cmd === "update") {
5615
+ const code = await runUpdate({ dryRun: hasFlag(args.slice(1), "--dry-run") });
5616
+ process.exit(code);
5617
+ }
5618
+ if (cmd === "skilify") {
5619
+ runSkilifyCommand(args.slice(1));
5620
+ return;
5621
+ }
5622
+ if (cmd === "embeddings") {
5623
+ const sub = args[1];
5624
+ if (sub === "install" || sub === "enable") {
5625
+ enableEmbeddings();
5626
+ return;
5627
+ }
5628
+ if (sub === "uninstall" || sub === "disable") {
5629
+ disableEmbeddings({ prune: hasFlag(args.slice(2), "--prune") });
5630
+ return;
5631
+ }
5632
+ if (sub === "status") {
5633
+ statusEmbeddings();
5634
+ return;
5635
+ }
5636
+ warn("Usage: hivemind embeddings install | uninstall [--prune] | status");
5637
+ process.exit(1);
5638
+ }
4386
5639
  if (AUTH_SUBCOMMANDS.has(cmd)) {
4387
5640
  await runAuthCommand(args);
4388
5641
  return;
@@ -4390,12 +5643,16 @@ async function main() {
4390
5643
  const platformCmds = ["claude", "codex", "claw", "cursor", "hermes", "pi"];
4391
5644
  if (platformCmds.includes(cmd)) {
4392
5645
  const sub = args[1];
4393
- if (sub === "install")
5646
+ if (sub === "install") {
4394
5647
  runSingleInstall(cmd);
4395
- else if (sub === "uninstall")
5648
+ if (hasFlag(args.slice(2), "--with-embeddings")) {
5649
+ log("");
5650
+ enableEmbeddings();
5651
+ }
5652
+ } else if (sub === "uninstall")
4396
5653
  runSingleUninstall(cmd);
4397
5654
  else {
4398
- warn(`Usage: hivemind ${cmd} install|uninstall`);
5655
+ warn(`Usage: hivemind ${cmd} install [--with-embeddings] | uninstall`);
4399
5656
  process.exit(1);
4400
5657
  }
4401
5658
  return;