@deeplake/hivemind 0.7.21 → 0.7.23

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 (37) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/README.md +1 -1
  4. package/bundle/cli.js +66 -15
  5. package/codex/bundle/capture.js +60 -12
  6. package/codex/bundle/commands/auth-login.js +4 -2
  7. package/codex/bundle/pre-tool-use.js +4 -2
  8. package/codex/bundle/session-start-setup.js +52 -5
  9. package/codex/bundle/session-start.js +62 -11
  10. package/codex/bundle/shell/deeplake-shell.js +4 -2
  11. package/codex/bundle/skillify-worker.js +118 -29
  12. package/codex/bundle/stop.js +100 -52
  13. package/codex/bundle/wiki-worker.js +7 -3
  14. package/cursor/bundle/capture.js +87 -39
  15. package/cursor/bundle/commands/auth-login.js +4 -2
  16. package/cursor/bundle/pre-tool-use.js +4 -2
  17. package/cursor/bundle/session-end.js +78 -34
  18. package/cursor/bundle/session-start.js +67 -15
  19. package/cursor/bundle/shell/deeplake-shell.js +4 -2
  20. package/cursor/bundle/skillify-worker.js +118 -29
  21. package/cursor/bundle/wiki-worker.js +7 -3
  22. package/hermes/bundle/capture.js +87 -39
  23. package/hermes/bundle/commands/auth-login.js +4 -2
  24. package/hermes/bundle/pre-tool-use.js +4 -2
  25. package/hermes/bundle/session-end.js +78 -34
  26. package/hermes/bundle/session-start.js +67 -15
  27. package/hermes/bundle/shell/deeplake-shell.js +4 -2
  28. package/hermes/bundle/skillify-worker.js +118 -29
  29. package/hermes/bundle/wiki-worker.js +7 -3
  30. package/mcp/bundle/server.js +4 -2
  31. package/openclaw/dist/index.js +8 -6
  32. package/openclaw/dist/skillify-worker.js +118 -29
  33. package/openclaw/openclaw.plugin.json +1 -1
  34. package/openclaw/package.json +1 -1
  35. package/openclaw/skills/SKILL.md +1 -1
  36. package/package.json +1 -1
  37. package/pi/extension-source/hivemind.ts +18 -3
@@ -112,7 +112,7 @@ function tryAcquireLock(sessionId, maxAgeMs = 10 * 60 * 1e3) {
112
112
  // dist/src/hooks/cursor/spawn-wiki-worker.js
113
113
  import { spawn, execSync } from "node:child_process";
114
114
  import { fileURLToPath } from "node:url";
115
- import { dirname, join as join5 } from "node:path";
115
+ import { dirname as dirname2, join as join6 } from "node:path";
116
116
  import { writeFileSync as writeFileSync2, mkdirSync as mkdirSync3 } from "node:fs";
117
117
  import { homedir as homedir4, tmpdir } from "node:os";
118
118
 
@@ -134,9 +134,51 @@ function makeWikiLogger(hooksDir, filename = "deeplake-wiki.log") {
134
134
  };
135
135
  }
136
136
 
137
+ // dist/src/utils/version-check.js
138
+ import { readFileSync as readFileSync3 } from "node:fs";
139
+ import { dirname, join as join5 } from "node:path";
140
+ function getInstalledVersion(bundleDir, pluginManifestDir) {
141
+ try {
142
+ const pluginJson = join5(bundleDir, "..", pluginManifestDir, "plugin.json");
143
+ const plugin = JSON.parse(readFileSync3(pluginJson, "utf-8"));
144
+ if (plugin.version)
145
+ return plugin.version;
146
+ } catch {
147
+ }
148
+ try {
149
+ const stamp = readFileSync3(join5(bundleDir, "..", ".hivemind_version"), "utf-8").trim();
150
+ if (stamp)
151
+ return stamp;
152
+ } catch {
153
+ }
154
+ const HIVEMIND_PKG_NAMES = /* @__PURE__ */ new Set([
155
+ "hivemind",
156
+ "hivemind-codex",
157
+ "@deeplake/hivemind",
158
+ "@deeplake/hivemind-codex",
159
+ "@activeloop/hivemind",
160
+ "@activeloop/hivemind-codex"
161
+ ]);
162
+ let dir = bundleDir;
163
+ for (let i = 0; i < 5; i++) {
164
+ const candidate = join5(dir, "package.json");
165
+ try {
166
+ const pkg = JSON.parse(readFileSync3(candidate, "utf-8"));
167
+ if (HIVEMIND_PKG_NAMES.has(pkg.name) && pkg.version)
168
+ return pkg.version;
169
+ } catch {
170
+ }
171
+ const parent = dirname(dir);
172
+ if (parent === dir)
173
+ break;
174
+ dir = parent;
175
+ }
176
+ return null;
177
+ }
178
+
137
179
  // dist/src/hooks/cursor/spawn-wiki-worker.js
138
180
  var HOME = homedir4();
139
- var wikiLogger = makeWikiLogger(join5(HOME, ".cursor", "hooks"));
181
+ var wikiLogger = makeWikiLogger(join6(HOME, ".cursor", "hooks"));
140
182
  var WIKI_LOG = wikiLogger.path;
141
183
  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.
142
184
 
@@ -198,9 +240,10 @@ function findCursorBin() {
198
240
  function spawnCursorWikiWorker(opts) {
199
241
  const { config, sessionId, cwd, bundleDir, reason } = opts;
200
242
  const projectName = cwd.split("/").pop() || "unknown";
201
- const tmpDir = join5(tmpdir(), `deeplake-wiki-${sessionId}-${Date.now()}`);
243
+ const tmpDir = join6(tmpdir(), `deeplake-wiki-${sessionId}-${Date.now()}`);
202
244
  mkdirSync3(tmpDir, { recursive: true });
203
- const configFile = join5(tmpDir, "config.json");
245
+ const pluginVersion = getInstalledVersion(bundleDir, ".claude-plugin") ?? "";
246
+ const configFile = join6(tmpDir, "config.json");
204
247
  writeFileSync2(configFile, JSON.stringify({
205
248
  apiUrl: config.apiUrl,
206
249
  token: config.token,
@@ -211,15 +254,16 @@ function spawnCursorWikiWorker(opts) {
211
254
  sessionId,
212
255
  userName: config.userName,
213
256
  project: projectName,
257
+ pluginVersion,
214
258
  tmpDir,
215
259
  cursorBin: findCursorBin(),
216
260
  cursorModel: process.env.HIVEMIND_CURSOR_MODEL ?? "auto",
217
261
  wikiLog: WIKI_LOG,
218
- hooksDir: join5(HOME, ".cursor", "hooks"),
262
+ hooksDir: join6(HOME, ".cursor", "hooks"),
219
263
  promptTemplate: WIKI_PROMPT_TEMPLATE
220
264
  }));
221
265
  wikiLog(`${reason}: spawning summary worker for ${sessionId}`);
222
- const workerPath = join5(bundleDir, "wiki-worker.js");
266
+ const workerPath = join6(bundleDir, "wiki-worker.js");
223
267
  spawn("nohup", ["node", workerPath, configFile], {
224
268
  detached: true,
225
269
  stdio: ["ignore", "ignore", "ignore"]
@@ -227,13 +271,13 @@ function spawnCursorWikiWorker(opts) {
227
271
  wikiLog(`${reason}: spawned summary worker for ${sessionId}`);
228
272
  }
229
273
  function bundleDirFromImportMeta(importMetaUrl) {
230
- return dirname(fileURLToPath(importMetaUrl));
274
+ return dirname2(fileURLToPath(importMetaUrl));
231
275
  }
232
276
 
233
277
  // dist/src/skillify/spawn-skillify-worker.js
234
278
  import { spawn as spawn2 } from "node:child_process";
235
279
  import { fileURLToPath as fileURLToPath2 } from "node:url";
236
- import { dirname as dirname2, join as join7 } from "node:path";
280
+ import { dirname as dirname3, join as join8 } from "node:path";
237
281
  import { writeFileSync as writeFileSync3, mkdirSync as mkdirSync4, appendFileSync as appendFileSync3, chmodSync } from "node:fs";
238
282
  import { homedir as homedir6, tmpdir as tmpdir2 } from "node:os";
239
283
 
@@ -241,7 +285,7 @@ import { homedir as homedir6, tmpdir as tmpdir2 } from "node:os";
241
285
  import { execFileSync } from "node:child_process";
242
286
  import { existsSync as existsSync3 } from "node:fs";
243
287
  import { homedir as homedir5 } from "node:os";
244
- import { join as join6 } from "node:path";
288
+ import { join as join7 } from "node:path";
245
289
  function findAgentBin(agent) {
246
290
  const which = (name) => {
247
291
  try {
@@ -256,24 +300,24 @@ function findAgentBin(agent) {
256
300
  };
257
301
  switch (agent) {
258
302
  case "claude_code":
259
- return which("claude") ?? join6(homedir5(), ".claude", "local", "claude");
303
+ return which("claude") ?? join7(homedir5(), ".claude", "local", "claude");
260
304
  case "codex":
261
305
  return which("codex") ?? "/usr/local/bin/codex";
262
306
  case "cursor":
263
307
  return which("cursor-agent") ?? "/usr/local/bin/cursor-agent";
264
308
  case "hermes":
265
- return which("hermes") ?? join6(homedir5(), ".local", "bin", "hermes");
309
+ return which("hermes") ?? join7(homedir5(), ".local", "bin", "hermes");
266
310
  case "pi":
267
- return which("pi") ?? join6(homedir5(), ".local", "bin", "pi");
311
+ return which("pi") ?? join7(homedir5(), ".local", "bin", "pi");
268
312
  }
269
313
  }
270
314
 
271
315
  // dist/src/skillify/spawn-skillify-worker.js
272
316
  var HOME2 = homedir6();
273
- var SKILLIFY_LOG = join7(HOME2, ".claude", "hooks", "skillify.log");
317
+ var SKILLIFY_LOG = join8(HOME2, ".claude", "hooks", "skillify.log");
274
318
  function skillifyLog(msg) {
275
319
  try {
276
- mkdirSync4(dirname2(SKILLIFY_LOG), { recursive: true });
320
+ mkdirSync4(dirname3(SKILLIFY_LOG), { recursive: true });
277
321
  appendFileSync3(SKILLIFY_LOG, `[${utcTimestamp()}] ${msg}
278
322
  `);
279
323
  } catch {
@@ -281,10 +325,10 @@ function skillifyLog(msg) {
281
325
  }
282
326
  function spawnSkillifyWorker(opts) {
283
327
  const { config, cwd, projectKey, project, bundleDir, agent, scopeConfig, currentSessionId, reason } = opts;
284
- const tmpDir = join7(tmpdir2(), `deeplake-skillify-${projectKey}-${Date.now()}`);
328
+ const tmpDir = join8(tmpdir2(), `deeplake-skillify-${projectKey}-${Date.now()}`);
285
329
  mkdirSync4(tmpDir, { recursive: true, mode: 448 });
286
330
  const gateBin = findAgentBin(agent);
287
- const configFile = join7(tmpDir, "config.json");
331
+ const configFile = join8(tmpDir, "config.json");
288
332
  writeFileSync3(configFile, JSON.stringify({
289
333
  apiUrl: config.apiUrl,
290
334
  token: config.token,
@@ -315,7 +359,7 @@ function spawnSkillifyWorker(opts) {
315
359
  } catch {
316
360
  }
317
361
  skillifyLog(`${reason}: spawning skillify worker for project=${project} key=${projectKey}`);
318
- const workerPath = join7(bundleDir, "skillify-worker.js");
362
+ const workerPath = join8(bundleDir, "skillify-worker.js");
319
363
  spawn2("nohup", ["node", workerPath, configFile], {
320
364
  detached: true,
321
365
  stdio: ["ignore", "ignore", "ignore"]
@@ -324,25 +368,25 @@ function spawnSkillifyWorker(opts) {
324
368
  }
325
369
 
326
370
  // dist/src/skillify/state.js
327
- import { readFileSync as readFileSync3, writeFileSync as writeFileSync4, writeSync as writeSync2, mkdirSync as mkdirSync5, renameSync as renameSync3, existsSync as existsSync5, unlinkSync as unlinkSync2, openSync as openSync2, closeSync as closeSync2 } from "node:fs";
371
+ import { readFileSync as readFileSync4, writeFileSync as writeFileSync4, writeSync as writeSync2, mkdirSync as mkdirSync5, renameSync as renameSync3, existsSync as existsSync5, unlinkSync as unlinkSync2, openSync as openSync2, closeSync as closeSync2 } from "node:fs";
328
372
  import { execSync as execSync2 } from "node:child_process";
329
373
  import { homedir as homedir8 } from "node:os";
330
374
  import { createHash } from "node:crypto";
331
- import { join as join9, basename } from "node:path";
375
+ import { join as join10, basename } from "node:path";
332
376
 
333
377
  // dist/src/skillify/legacy-migration.js
334
378
  import { existsSync as existsSync4, renameSync as renameSync2 } from "node:fs";
335
379
  import { homedir as homedir7 } from "node:os";
336
- import { join as join8 } from "node:path";
380
+ import { join as join9 } from "node:path";
337
381
  var dlog2 = (msg) => log("skillify-migrate", msg);
338
382
  var attempted = false;
339
383
  function migrateLegacyStateDir() {
340
384
  if (attempted)
341
385
  return;
342
386
  attempted = true;
343
- const root = join8(homedir7(), ".deeplake", "state");
344
- const legacy = join8(root, "skilify");
345
- const current = join8(root, "skillify");
387
+ const root = join9(homedir7(), ".deeplake", "state");
388
+ const legacy = join9(root, "skilify");
389
+ const current = join9(root, "skillify");
346
390
  if (!existsSync4(legacy))
347
391
  return;
348
392
  if (existsSync4(current))
@@ -362,17 +406,17 @@ function migrateLegacyStateDir() {
362
406
 
363
407
  // dist/src/skillify/state.js
364
408
  var dlog3 = (msg) => log("skillify-state", msg);
365
- var STATE_DIR2 = join9(homedir8(), ".deeplake", "state", "skillify");
409
+ var STATE_DIR2 = join10(homedir8(), ".deeplake", "state", "skillify");
366
410
  var YIELD_BUF2 = new Int32Array(new SharedArrayBuffer(4));
367
411
  var TRIGGER_THRESHOLD = (() => {
368
412
  const n = Number(process.env.HIVEMIND_SKILLIFY_EVERY_N_TURNS ?? "");
369
413
  return Number.isInteger(n) && n > 0 ? n : 20;
370
414
  })();
371
415
  function statePath(projectKey) {
372
- return join9(STATE_DIR2, `${projectKey}.json`);
416
+ return join10(STATE_DIR2, `${projectKey}.json`);
373
417
  }
374
418
  function lockPath2(projectKey) {
375
- return join9(STATE_DIR2, `${projectKey}.lock`);
419
+ return join10(STATE_DIR2, `${projectKey}.lock`);
376
420
  }
377
421
  var DEFAULT_PORTS = {
378
422
  http: "80",
@@ -421,7 +465,7 @@ function readState(projectKey) {
421
465
  if (!existsSync5(p))
422
466
  return null;
423
467
  try {
424
- return JSON.parse(readFileSync3(p, "utf-8"));
468
+ return JSON.parse(readFileSync4(p, "utf-8"));
425
469
  } catch {
426
470
  return null;
427
471
  }
@@ -483,7 +527,7 @@ function tryAcquireWorkerLock(projectKey, maxAgeMs = 10 * 60 * 1e3) {
483
527
  const p = lockPath2(projectKey);
484
528
  if (existsSync5(p)) {
485
529
  try {
486
- const ageMs = Date.now() - parseInt(readFileSync3(p, "utf-8"), 10);
530
+ const ageMs = Date.now() - parseInt(readFileSync4(p, "utf-8"), 10);
487
531
  if (Number.isFinite(ageMs) && ageMs < maxAgeMs)
488
532
  return false;
489
533
  } catch (readErr) {
@@ -517,19 +561,19 @@ function releaseWorkerLock(projectKey) {
517
561
  }
518
562
 
519
563
  // dist/src/skillify/scope-config.js
520
- import { existsSync as existsSync6, mkdirSync as mkdirSync6, readFileSync as readFileSync4, writeFileSync as writeFileSync5 } from "node:fs";
564
+ import { existsSync as existsSync6, mkdirSync as mkdirSync6, readFileSync as readFileSync5, writeFileSync as writeFileSync5 } from "node:fs";
521
565
  import { homedir as homedir9 } from "node:os";
522
- import { join as join10 } from "node:path";
523
- var STATE_DIR3 = join10(homedir9(), ".deeplake", "state", "skillify");
524
- var CONFIG_PATH = join10(STATE_DIR3, "config.json");
566
+ import { join as join11 } from "node:path";
567
+ var STATE_DIR3 = join11(homedir9(), ".deeplake", "state", "skillify");
568
+ var CONFIG_PATH = join11(STATE_DIR3, "config.json");
525
569
  var DEFAULT = { scope: "me", team: [], install: "project" };
526
570
  function loadScopeConfig() {
527
571
  migrateLegacyStateDir();
528
572
  if (!existsSync6(CONFIG_PATH))
529
573
  return DEFAULT;
530
574
  try {
531
- const raw = JSON.parse(readFileSync4(CONFIG_PATH, "utf-8"));
532
- const scope = raw.scope === "team" || raw.scope === "org" ? raw.scope : "me";
575
+ const raw = JSON.parse(readFileSync5(CONFIG_PATH, "utf-8"));
576
+ const scope = raw.scope === "team" ? "team" : raw.scope === "org" ? "team" : "me";
533
577
  const team = Array.isArray(raw.team) ? raw.team.filter((s) => typeof s === "string") : [];
534
578
  const install = raw.install === "global" ? "global" : "project";
535
579
  return { scope, team, install };
@@ -512,13 +512,14 @@ var DeeplakeApi = class {
512
512
  const tables = await this.listTables();
513
513
  if (!tables.includes(tbl)) {
514
514
  log2(`table "${tbl}" not found, creating`);
515
- 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);
515
+ 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
516
  log2(`table "${tbl}" created`);
517
517
  if (!tables.includes(tbl))
518
518
  this._tablesCache = [...tables, tbl];
519
519
  }
520
520
  await this.ensureEmbeddingColumn(tbl, SUMMARY_EMBEDDING_COL);
521
521
  await this.ensureColumn(tbl, "agent", "TEXT NOT NULL DEFAULT ''");
522
+ await this.ensureColumn(tbl, "plugin_version", "TEXT NOT NULL DEFAULT ''");
522
523
  }
523
524
  /** Create the sessions table (uses JSONB for message since every row is a JSON event). */
524
525
  async ensureSessionsTable(name) {
@@ -526,13 +527,14 @@ var DeeplakeApi = class {
526
527
  const tables = await this.listTables();
527
528
  if (!tables.includes(safe)) {
528
529
  log2(`table "${safe}" not found, creating`);
529
- 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);
530
+ 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);
530
531
  log2(`table "${safe}" created`);
531
532
  if (!tables.includes(safe))
532
533
  this._tablesCache = [...tables, safe];
533
534
  }
534
535
  await this.ensureEmbeddingColumn(safe, MESSAGE_EMBEDDING_COL);
535
536
  await this.ensureColumn(safe, "agent", "TEXT NOT NULL DEFAULT ''");
537
+ await this.ensureColumn(safe, "plugin_version", "TEXT NOT NULL DEFAULT ''");
536
538
  await this.ensureLookupIndex(safe, "path_creation_date", `("path", "creation_date")`);
537
539
  }
538
540
  /**
@@ -703,18 +705,25 @@ function parseFrontmatter(text) {
703
705
  const head = text.slice(4, end).trim();
704
706
  const body = text.slice(end + 4).replace(/^\r?\n/, "");
705
707
  const fm = { source_sessions: [] };
706
- let mode = "kv";
708
+ let arrayKey = null;
707
709
  for (const raw of head.split(/\r?\n/)) {
708
- if (mode === "sources") {
710
+ if (arrayKey) {
709
711
  const m2 = raw.match(/^\s+-\s+(.+)$/);
710
712
  if (m2) {
711
- fm.source_sessions.push(m2[1].trim());
713
+ const arr = fm[arrayKey] ?? [];
714
+ arr.push(m2[1].trim());
715
+ fm[arrayKey] = arr;
712
716
  continue;
713
717
  }
714
- mode = "kv";
718
+ arrayKey = null;
715
719
  }
716
720
  if (raw.startsWith("source_sessions:")) {
717
- mode = "sources";
721
+ arrayKey = "source_sessions";
722
+ continue;
723
+ }
724
+ if (raw.startsWith("contributors:")) {
725
+ arrayKey = "contributors";
726
+ fm.contributors = [];
718
727
  continue;
719
728
  }
720
729
  const m = raw.match(/^([a-zA-Z_]+):\s*(.*)$/);
@@ -929,11 +938,19 @@ function buildPullSql(args) {
929
938
  where.push(`name = '${esc(args.skillName)}'`);
930
939
  }
931
940
  const whereClause = where.length > 0 ? ` WHERE ${where.join(" AND ")}` : "";
932
- 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`;
941
+ const contributorsCol = args.includeContributors === false ? "" : "contributors, ";
942
+ return `SELECT name, project, project_key, body, version, source_agent, scope, author, ${contributorsCol}description, trigger_text, source_sessions, install, created_at, updated_at FROM "${args.tableName}"${whereClause} ORDER BY project_key ASC, name ASC, version DESC`;
943
+ }
944
+ function isMissingContributorsColumnError(message) {
945
+ if (!message)
946
+ return false;
947
+ return /contributors.*(?:does not exist|not found|unknown)/i.test(message) || /(?:does not exist|unknown column).*contributors/i.test(message);
933
948
  }
934
949
  function isMissingTableError(message) {
935
950
  if (!message)
936
951
  return false;
952
+ if (/\bcolumn\b/i.test(message))
953
+ return false;
937
954
  return /Table does not exist|relation .* does not exist|no such table/i.test(message);
938
955
  }
939
956
  function resolvePullDestination(install, cwd) {
@@ -1029,11 +1046,16 @@ function selectLatestPerName(rows) {
1029
1046
  }
1030
1047
  function renderSkillFile(row) {
1031
1048
  const sources = parseSourceSessions(row.source_sessions);
1049
+ const author = typeof row.author === "string" && row.author.length > 0 ? row.author : void 0;
1050
+ const contributors = parseContributors(row.contributors);
1051
+ const renderedContributors = contributors.length > 0 ? contributors : author ? [author] : [];
1032
1052
  const fm = {
1033
1053
  name: String(row.name ?? ""),
1034
1054
  description: String(row.description ?? ""),
1035
1055
  trigger: typeof row.trigger_text === "string" && row.trigger_text.length > 0 ? String(row.trigger_text) : void 0,
1056
+ author,
1036
1057
  source_sessions: sources,
1058
+ contributors: renderedContributors,
1037
1059
  version: Number(row.version ?? 1),
1038
1060
  created_by_agent: String(row.source_agent ?? "unknown"),
1039
1061
  created_at: String(row.created_at ?? (/* @__PURE__ */ new Date()).toISOString()),
@@ -1058,15 +1080,35 @@ function parseSourceSessions(v) {
1058
1080
  }
1059
1081
  return [];
1060
1082
  }
1083
+ function parseContributors(v) {
1084
+ if (Array.isArray(v))
1085
+ return v.map(String);
1086
+ if (typeof v === "string") {
1087
+ try {
1088
+ const parsed = JSON.parse(v);
1089
+ if (Array.isArray(parsed))
1090
+ return parsed.map(String);
1091
+ } catch {
1092
+ }
1093
+ }
1094
+ return [];
1095
+ }
1061
1096
  function renderFrontmatter(fm) {
1062
1097
  const lines = ["---"];
1063
1098
  lines.push(`name: ${fm.name}`);
1064
1099
  lines.push(`description: ${JSON.stringify(fm.description)}`);
1065
1100
  if (fm.trigger)
1066
1101
  lines.push(`trigger: ${JSON.stringify(fm.trigger)}`);
1102
+ if (fm.author)
1103
+ lines.push(`author: ${fm.author}`);
1067
1104
  lines.push(`source_sessions:`);
1068
1105
  for (const s of fm.source_sessions)
1069
1106
  lines.push(` - ${s}`);
1107
+ if (fm.contributors && fm.contributors.length > 0) {
1108
+ lines.push(`contributors:`);
1109
+ for (const c of fm.contributors)
1110
+ lines.push(` - ${c}`);
1111
+ }
1070
1112
  lines.push(`version: ${fm.version}`);
1071
1113
  lines.push(`created_by_agent: ${fm.created_by_agent}`);
1072
1114
  lines.push(`created_at: ${fm.created_at}`);
@@ -1106,10 +1148,19 @@ async function runPull(opts) {
1106
1148
  try {
1107
1149
  rows = await opts.query(sql);
1108
1150
  } catch (e) {
1109
- if (isMissingTableError(e?.message))
1151
+ if (isMissingTableError(e?.message)) {
1110
1152
  rows = [];
1111
- else
1153
+ } else if (isMissingContributorsColumnError(e?.message)) {
1154
+ const legacySql = buildPullSql({
1155
+ tableName: opts.tableName,
1156
+ users: opts.users,
1157
+ skillName: opts.skillName,
1158
+ includeContributors: false
1159
+ });
1160
+ rows = await opts.query(legacySql);
1161
+ } else {
1112
1162
  throw e;
1163
+ }
1113
1164
  }
1114
1165
  const latest = selectLatestPerName(rows);
1115
1166
  const root = resolvePullDestination(opts.install, opts.cwd);
@@ -1312,7 +1363,7 @@ SKILLS (skillify) \u2014 mine + share reusable skills across the org:
1312
1363
  - hivemind skillify unpull --user <email> \u2014 remove only that author's pulls
1313
1364
  - hivemind skillify unpull --not-mine \u2014 remove all pulls except your own
1314
1365
  - hivemind skillify unpull --dry-run \u2014 preview without touching disk
1315
- - hivemind skillify scope <me|team|org> \u2014 sharing scope for new skills
1366
+ - hivemind skillify scope <me|team> \u2014 sharing scope for new skills
1316
1367
  - hivemind skillify install <project|global> \u2014 default install location
1317
1368
  - hivemind skillify team add|remove|list <name> \u2014 manage team list`;
1318
1369
  function resolveSessionId(input) {
@@ -1325,7 +1376,7 @@ function resolveCwd(input) {
1325
1376
  }
1326
1377
  return process.cwd();
1327
1378
  }
1328
- async function createPlaceholder(api, table, sessionId, cwd, userName, orgName, workspaceId) {
1379
+ async function createPlaceholder(api, table, sessionId, cwd, userName, orgName, workspaceId, pluginVersion) {
1329
1380
  const summaryPath = `/summaries/${userName}/${sessionId}.md`;
1330
1381
  const existing = await api.query(`SELECT path FROM "${table}" WHERE path = '${sqlStr(summaryPath)}' LIMIT 1`);
1331
1382
  if (existing.length > 0)
@@ -1342,7 +1393,7 @@ async function createPlaceholder(api, table, sessionId, cwd, userName, orgName,
1342
1393
  ""
1343
1394
  ].join("\n");
1344
1395
  const filename = `${sessionId}.md`;
1345
- await api.query(`INSERT INTO "${table}" (id, path, filename, summary, author, mime_type, size_bytes, project, description, agent, creation_date, last_update_date) VALUES ('${crypto.randomUUID()}', '${sqlStr(summaryPath)}', '${sqlStr(filename)}', E'${sqlStr(content)}', '${sqlStr(userName)}', 'text/markdown', ${Buffer.byteLength(content, "utf-8")}, '${sqlStr(projectName)}', 'in progress', 'cursor', '${now}', '${now}')`);
1396
+ await api.query(`INSERT INTO "${table}" (id, path, filename, summary, author, mime_type, size_bytes, project, description, agent, plugin_version, creation_date, last_update_date) VALUES ('${crypto.randomUUID()}', '${sqlStr(summaryPath)}', '${sqlStr(filename)}', E'${sqlStr(content)}', '${sqlStr(userName)}', 'text/markdown', ${Buffer.byteLength(content, "utf-8")}, '${sqlStr(projectName)}', 'in progress', 'cursor', '${sqlStr(pluginVersion)}', '${now}', '${now}')`);
1346
1397
  }
1347
1398
  async function main() {
1348
1399
  if (process.env.HIVEMIND_WIKI_WORKER === "1")
@@ -1357,6 +1408,8 @@ async function main() {
1357
1408
  log5(`credentials loaded: org=${creds.orgName ?? creds.orgId}`);
1358
1409
  }
1359
1410
  await autoUpdate(creds, { agent: "cursor" });
1411
+ const current = getInstalledVersion(__bundleDir, ".claude-plugin");
1412
+ const pluginVersion = current ?? "";
1360
1413
  const captureEnabled = process.env.HIVEMIND_CAPTURE !== "false";
1361
1414
  if (creds?.token && captureEnabled) {
1362
1415
  try {
@@ -1367,7 +1420,7 @@ async function main() {
1367
1420
  const api = new DeeplakeApi(config.token, config.apiUrl, config.orgId, config.workspaceId, table);
1368
1421
  await api.ensureTable();
1369
1422
  await api.ensureSessionsTable(sessionsTable);
1370
- await createPlaceholder(api, table, sessionId, cwd, config.userName, config.orgName, config.workspaceId);
1423
+ await createPlaceholder(api, table, sessionId, cwd, config.userName, config.orgName, config.workspaceId, pluginVersion);
1371
1424
  log5("placeholder created");
1372
1425
  }
1373
1426
  } catch (e) {
@@ -1377,7 +1430,6 @@ async function main() {
1377
1430
  const pullResult = await autoPullSkills();
1378
1431
  log5(`autopull: pulled=${pullResult.pulled} skipped=${pullResult.skipped}`);
1379
1432
  let versionNotice = "";
1380
- const current = getInstalledVersion(__bundleDir, ".claude-plugin");
1381
1433
  if (current)
1382
1434
  versionNotice = `
1383
1435
  Hivemind v${current}`;
@@ -67201,13 +67201,14 @@ var DeeplakeApi = class {
67201
67201
  const tables = await this.listTables();
67202
67202
  if (!tables.includes(tbl)) {
67203
67203
  log2(`table "${tbl}" not found, creating`);
67204
- 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);
67204
+ 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);
67205
67205
  log2(`table "${tbl}" created`);
67206
67206
  if (!tables.includes(tbl))
67207
67207
  this._tablesCache = [...tables, tbl];
67208
67208
  }
67209
67209
  await this.ensureEmbeddingColumn(tbl, SUMMARY_EMBEDDING_COL);
67210
67210
  await this.ensureColumn(tbl, "agent", "TEXT NOT NULL DEFAULT ''");
67211
+ await this.ensureColumn(tbl, "plugin_version", "TEXT NOT NULL DEFAULT ''");
67211
67212
  }
67212
67213
  /** Create the sessions table (uses JSONB for message since every row is a JSON event). */
67213
67214
  async ensureSessionsTable(name) {
@@ -67215,13 +67216,14 @@ var DeeplakeApi = class {
67215
67216
  const tables = await this.listTables();
67216
67217
  if (!tables.includes(safe)) {
67217
67218
  log2(`table "${safe}" not found, creating`);
67218
- 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);
67219
+ 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);
67219
67220
  log2(`table "${safe}" created`);
67220
67221
  if (!tables.includes(safe))
67221
67222
  this._tablesCache = [...tables, safe];
67222
67223
  }
67223
67224
  await this.ensureEmbeddingColumn(safe, MESSAGE_EMBEDDING_COL);
67224
67225
  await this.ensureColumn(safe, "agent", "TEXT NOT NULL DEFAULT ''");
67226
+ await this.ensureColumn(safe, "plugin_version", "TEXT NOT NULL DEFAULT ''");
67225
67227
  await this.ensureLookupIndex(safe, "path_creation_date", `("path", "creation_date")`);
67226
67228
  }
67227
67229
  /**