@deeplake/hivemind 0.7.4 → 0.7.11

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 (35) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/README.md +97 -0
  4. package/bundle/cli.js +820 -20
  5. package/codex/bundle/capture.js +40 -10
  6. package/codex/bundle/commands/auth-login.js +84 -18
  7. package/codex/bundle/pre-tool-use.js +41 -11
  8. package/codex/bundle/session-start-setup.js +40 -10
  9. package/codex/bundle/session-start.js +27 -3
  10. package/codex/bundle/shell/deeplake-shell.js +41 -11
  11. package/codex/bundle/skilify-worker.js +907 -0
  12. package/codex/bundle/stop.js +373 -51
  13. package/cursor/bundle/capture.js +354 -13
  14. package/cursor/bundle/commands/auth-login.js +84 -18
  15. package/cursor/bundle/pre-tool-use.js +40 -10
  16. package/cursor/bundle/session-end.js +303 -6
  17. package/cursor/bundle/session-start.js +68 -14
  18. package/cursor/bundle/shell/deeplake-shell.js +41 -11
  19. package/cursor/bundle/skilify-worker.js +907 -0
  20. package/hermes/bundle/capture.js +354 -13
  21. package/hermes/bundle/commands/auth-login.js +84 -18
  22. package/hermes/bundle/pre-tool-use.js +40 -10
  23. package/hermes/bundle/session-end.js +305 -7
  24. package/hermes/bundle/session-start.js +68 -14
  25. package/hermes/bundle/shell/deeplake-shell.js +41 -11
  26. package/hermes/bundle/skilify-worker.js +907 -0
  27. package/mcp/bundle/server.js +41 -11
  28. package/openclaw/dist/chunks/{config-G23NI5TV.js → config-ZLH6JFJS.js} +1 -0
  29. package/openclaw/dist/index.js +185 -16
  30. package/openclaw/dist/skilify-worker.js +907 -0
  31. package/openclaw/openclaw.plugin.json +1 -1
  32. package/openclaw/package.json +2 -2
  33. package/openclaw/skills/SKILL.md +19 -0
  34. package/package.json +6 -1
  35. package/pi/extension-source/hivemind.ts +130 -1
@@ -97,6 +97,7 @@ function loadConfig() {
97
97
  apiUrl: process.env.HIVEMIND_API_URL ?? creds?.apiUrl ?? "https://api.deeplake.ai",
98
98
  tableName: process.env.HIVEMIND_TABLE ?? "memory",
99
99
  sessionsTableName: process.env.HIVEMIND_SESSIONS_TABLE ?? "sessions",
100
+ skillsTableName: process.env.HIVEMIND_SKILLS_TABLE ?? "skills",
100
101
  memoryPath: process.env.HIVEMIND_MEMORY_PATH ?? join(home, ".deeplake", "memory")
101
102
  };
102
103
  }
@@ -124,6 +125,12 @@ function log(tag, msg) {
124
125
  function sqlStr(value) {
125
126
  return value.replace(/\\/g, "\\\\").replace(/'/g, "''").replace(/\0/g, "").replace(/[\x01-\x08\x0b\x0c\x0e-\x1f\x7f]/g, "");
126
127
  }
128
+ function sqlIdent(name) {
129
+ if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name)) {
130
+ throw new Error(`Invalid SQL identifier: ${JSON.stringify(name)}`);
131
+ }
132
+ return name;
133
+ }
127
134
 
128
135
  // dist/src/embeddings/columns.js
129
136
  var SUMMARY_EMBEDDING_COL = "summary_embedding";
@@ -496,7 +503,7 @@ var DeeplakeApi = class {
496
503
  }
497
504
  /** Create the memory table if it doesn't already exist. Migrate columns on existing tables. */
498
505
  async ensureTable(name) {
499
- const tbl = name ?? this.tableName;
506
+ const tbl = sqlIdent(name ?? this.tableName);
500
507
  const tables = await this.listTables();
501
508
  if (!tables.includes(tbl)) {
502
509
  log2(`table "${tbl}" not found, creating`);
@@ -510,17 +517,40 @@ var DeeplakeApi = class {
510
517
  }
511
518
  /** Create the sessions table (uses JSONB for message since every row is a JSON event). */
512
519
  async ensureSessionsTable(name) {
520
+ const safe = sqlIdent(name);
521
+ const tables = await this.listTables();
522
+ if (!tables.includes(safe)) {
523
+ log2(`table "${safe}" not found, creating`);
524
+ 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);
525
+ log2(`table "${safe}" created`);
526
+ if (!tables.includes(safe))
527
+ this._tablesCache = [...tables, safe];
528
+ }
529
+ await this.ensureEmbeddingColumn(safe, MESSAGE_EMBEDDING_COL);
530
+ await this.ensureColumn(safe, "agent", "TEXT NOT NULL DEFAULT ''");
531
+ await this.ensureLookupIndex(safe, "path_creation_date", `("path", "creation_date")`);
532
+ }
533
+ /**
534
+ * Create the skills table.
535
+ *
536
+ * One row per skill version. Workers INSERT a fresh row on every KEEP /
537
+ * MERGE rather than UPDATE-ing in place, so the full version history is
538
+ * recoverable. Uniqueness in the *current* state is by (project_key, name)
539
+ * — newer rows shadow older ones at read time (ORDER BY version DESC).
540
+ * This sidesteps the Deeplake UPDATE-coalescing quirk that bit the wiki
541
+ * worker.
542
+ */
543
+ async ensureSkillsTable(name) {
544
+ const safe = sqlIdent(name);
513
545
  const tables = await this.listTables();
514
- if (!tables.includes(name)) {
515
- log2(`table "${name}" not found, creating`);
516
- await this.createTableWithRetry(`CREATE TABLE IF NOT EXISTS "${name}" (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`, name);
517
- log2(`table "${name}" created`);
518
- if (!tables.includes(name))
519
- this._tablesCache = [...tables, name];
546
+ if (!tables.includes(safe)) {
547
+ log2(`table "${safe}" not found, creating`);
548
+ 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);
549
+ log2(`table "${safe}" created`);
550
+ if (!tables.includes(safe))
551
+ this._tablesCache = [...tables, safe];
520
552
  }
521
- await this.ensureEmbeddingColumn(name, MESSAGE_EMBEDDING_COL);
522
- await this.ensureColumn(name, "agent", "TEXT NOT NULL DEFAULT ''");
523
- await this.ensureLookupIndex(name, "path_creation_date", `("path", "creation_date")`);
553
+ await this.ensureLookupIndex(safe, "project_key_name", `("project_key", "name")`);
524
554
  }
525
555
  };
526
556
 
@@ -299,6 +299,7 @@ function loadConfig() {
299
299
  apiUrl: process.env.HIVEMIND_API_URL ?? creds?.apiUrl ?? "https://api.deeplake.ai",
300
300
  tableName: process.env.HIVEMIND_TABLE ?? "memory",
301
301
  sessionsTableName: process.env.HIVEMIND_SESSIONS_TABLE ?? "sessions",
302
+ skillsTableName: process.env.HIVEMIND_SKILLS_TABLE ?? "skills",
302
303
  memoryPath: process.env.HIVEMIND_MEMORY_PATH ?? join2(home, ".deeplake", "memory")
303
304
  };
304
305
  }
@@ -323,6 +324,12 @@ function log(tag, msg) {
323
324
  function sqlStr(value) {
324
325
  return value.replace(/\\/g, "\\\\").replace(/'/g, "''").replace(/\0/g, "").replace(/[\x01-\x08\x0b\x0c\x0e-\x1f\x7f]/g, "");
325
326
  }
327
+ function sqlIdent(name) {
328
+ if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name)) {
329
+ throw new Error(`Invalid SQL identifier: ${JSON.stringify(name)}`);
330
+ }
331
+ return name;
332
+ }
326
333
 
327
334
  // dist/src/embeddings/columns.js
328
335
  var SUMMARY_EMBEDDING_COL = "summary_embedding";
@@ -686,7 +693,7 @@ var DeeplakeApi = class {
686
693
  }
687
694
  /** Create the memory table if it doesn't already exist. Migrate columns on existing tables. */
688
695
  async ensureTable(name) {
689
- const tbl = name ?? this.tableName;
696
+ const tbl = sqlIdent(name ?? this.tableName);
690
697
  const tables = await this.listTables();
691
698
  if (!tables.includes(tbl)) {
692
699
  log2(`table "${tbl}" not found, creating`);
@@ -700,17 +707,40 @@ var DeeplakeApi = class {
700
707
  }
701
708
  /** Create the sessions table (uses JSONB for message since every row is a JSON event). */
702
709
  async ensureSessionsTable(name) {
710
+ const safe = sqlIdent(name);
703
711
  const tables = await this.listTables();
704
- if (!tables.includes(name)) {
705
- log2(`table "${name}" not found, creating`);
706
- await this.createTableWithRetry(`CREATE TABLE IF NOT EXISTS "${name}" (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`, name);
707
- log2(`table "${name}" created`);
708
- if (!tables.includes(name))
709
- this._tablesCache = [...tables, name];
712
+ if (!tables.includes(safe)) {
713
+ log2(`table "${safe}" not found, creating`);
714
+ 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);
715
+ log2(`table "${safe}" created`);
716
+ if (!tables.includes(safe))
717
+ this._tablesCache = [...tables, safe];
710
718
  }
711
- await this.ensureEmbeddingColumn(name, MESSAGE_EMBEDDING_COL);
712
- await this.ensureColumn(name, "agent", "TEXT NOT NULL DEFAULT ''");
713
- await this.ensureLookupIndex(name, "path_creation_date", `("path", "creation_date")`);
719
+ await this.ensureEmbeddingColumn(safe, MESSAGE_EMBEDDING_COL);
720
+ await this.ensureColumn(safe, "agent", "TEXT NOT NULL DEFAULT ''");
721
+ await this.ensureLookupIndex(safe, "path_creation_date", `("path", "creation_date")`);
722
+ }
723
+ /**
724
+ * Create the skills table.
725
+ *
726
+ * One row per skill version. Workers INSERT a fresh row on every KEEP /
727
+ * MERGE rather than UPDATE-ing in place, so the full version history is
728
+ * recoverable. Uniqueness in the *current* state is by (project_key, name)
729
+ * — newer rows shadow older ones at read time (ORDER BY version DESC).
730
+ * This sidesteps the Deeplake UPDATE-coalescing quirk that bit the wiki
731
+ * worker.
732
+ */
733
+ async ensureSkillsTable(name) {
734
+ const safe = sqlIdent(name);
735
+ const tables = await this.listTables();
736
+ if (!tables.includes(safe)) {
737
+ log2(`table "${safe}" not found, creating`);
738
+ 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);
739
+ log2(`table "${safe}" created`);
740
+ if (!tables.includes(safe))
741
+ this._tablesCache = [...tables, safe];
742
+ }
743
+ await this.ensureLookupIndex(safe, "project_key_name", `("project_key", "name")`);
714
744
  }
715
745
  };
716
746
 
@@ -890,8 +920,24 @@ async function runAuthCommand(args) {
890
920
  console.log(`Org not found: ${target}`);
891
921
  process.exit(1);
892
922
  }
923
+ const prevWs = creds.workspaceId ?? "default";
924
+ const lcPrev = prevWs.toLowerCase();
925
+ const wsList = await listWorkspaces(creds.token, apiUrl, match.id);
926
+ const matchedWs = wsList.find((w) => w.id === prevWs || w.name && w.name.toLowerCase() === lcPrev);
893
927
  await switchOrg(match.id, match.name);
894
928
  console.log(`Switched to org: ${match.name}`);
929
+ if (!matchedWs) {
930
+ if (prevWs !== "default") {
931
+ await switchWorkspace("default");
932
+ console.log(`Workspace '${prevWs}' is not in org '${match.name}'. Reset workspace to 'default'.`);
933
+ if (wsList.length > 0) {
934
+ console.log(`Available workspaces: ${wsList.map((w) => w.name || w.id).join(", ")}`);
935
+ }
936
+ }
937
+ } else if (matchedWs.id !== prevWs) {
938
+ await switchWorkspace(matchedWs.id);
939
+ console.log(`Workspace name '${prevWs}' resolved to id '${matchedWs.id}' in org '${match.name}'.`);
940
+ }
895
941
  } else {
896
942
  console.log("Usage: org list | org switch <name-or-id>");
897
943
  }
@@ -903,7 +949,7 @@ async function runAuthCommand(args) {
903
949
  process.exit(1);
904
950
  }
905
951
  const ws = await listWorkspaces(creds.token, apiUrl, creds.orgId);
906
- ws.forEach((w) => console.log(`${w.id} ${w.name}`));
952
+ ws.forEach((w) => console.log(w.name || w.id));
907
953
  break;
908
954
  }
909
955
  case "workspace": {
@@ -911,14 +957,34 @@ async function runAuthCommand(args) {
911
957
  console.log("Not logged in.");
912
958
  process.exit(1);
913
959
  }
914
- const wsId = args[1];
915
- if (!wsId) {
916
- console.log("Usage: workspace <id>");
917
- process.exit(1);
960
+ const sub = args[1];
961
+ if (sub === "list") {
962
+ const wsList = await listWorkspaces(creds.token, apiUrl, creds.orgId);
963
+ wsList.forEach((w) => console.log(w.name || w.id));
964
+ break;
918
965
  }
919
- await switchWorkspace(wsId);
920
- console.log(`Switched to workspace: ${wsId}`);
921
- break;
966
+ if (sub === "switch") {
967
+ const target = args[2];
968
+ if (!target) {
969
+ console.log("Usage: workspace switch <name-or-id>");
970
+ process.exit(1);
971
+ }
972
+ const wsList = await listWorkspaces(creds.token, apiUrl, creds.orgId);
973
+ const lcTarget = target.toLowerCase();
974
+ const match = wsList.find((w) => w.id === target || w.name && w.name.toLowerCase() === lcTarget);
975
+ if (!match) {
976
+ console.log(`Workspace not found: ${target}`);
977
+ if (wsList.length > 0) {
978
+ console.log(`Available workspaces: ${wsList.map((w) => w.name || w.id).join(", ")}`);
979
+ }
980
+ process.exit(1);
981
+ }
982
+ await switchWorkspace(match.id);
983
+ console.log(`Switched to workspace: ${match.name || match.id}`);
984
+ break;
985
+ }
986
+ console.log("Usage: workspace list | workspace switch <name-or-id>");
987
+ process.exit(1);
922
988
  }
923
989
  case "invite": {
924
990
  if (!creds) {
@@ -103,6 +103,7 @@ function loadConfig() {
103
103
  apiUrl: process.env.HIVEMIND_API_URL ?? creds?.apiUrl ?? "https://api.deeplake.ai",
104
104
  tableName: process.env.HIVEMIND_TABLE ?? "memory",
105
105
  sessionsTableName: process.env.HIVEMIND_SESSIONS_TABLE ?? "sessions",
106
+ skillsTableName: process.env.HIVEMIND_SKILLS_TABLE ?? "skills",
106
107
  memoryPath: process.env.HIVEMIND_MEMORY_PATH ?? join(home, ".deeplake", "memory")
107
108
  };
108
109
  }
@@ -130,6 +131,12 @@ function sqlStr(value) {
130
131
  function sqlLike(value) {
131
132
  return sqlStr(value).replace(/%/g, "\\%").replace(/_/g, "\\_");
132
133
  }
134
+ function sqlIdent(name) {
135
+ if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name)) {
136
+ throw new Error(`Invalid SQL identifier: ${JSON.stringify(name)}`);
137
+ }
138
+ return name;
139
+ }
133
140
 
134
141
  // dist/src/embeddings/columns.js
135
142
  var SUMMARY_EMBEDDING_COL = "summary_embedding";
@@ -502,7 +509,7 @@ var DeeplakeApi = class {
502
509
  }
503
510
  /** Create the memory table if it doesn't already exist. Migrate columns on existing tables. */
504
511
  async ensureTable(name) {
505
- const tbl = name ?? this.tableName;
512
+ const tbl = sqlIdent(name ?? this.tableName);
506
513
  const tables = await this.listTables();
507
514
  if (!tables.includes(tbl)) {
508
515
  log2(`table "${tbl}" not found, creating`);
@@ -516,17 +523,40 @@ var DeeplakeApi = class {
516
523
  }
517
524
  /** Create the sessions table (uses JSONB for message since every row is a JSON event). */
518
525
  async ensureSessionsTable(name) {
526
+ const safe = sqlIdent(name);
527
+ const tables = await this.listTables();
528
+ if (!tables.includes(safe)) {
529
+ log2(`table "${safe}" not found, creating`);
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 '', creation_date TEXT NOT NULL DEFAULT '', last_update_date TEXT NOT NULL DEFAULT '') USING deeplake`, safe);
531
+ log2(`table "${safe}" created`);
532
+ if (!tables.includes(safe))
533
+ this._tablesCache = [...tables, safe];
534
+ }
535
+ await this.ensureEmbeddingColumn(safe, MESSAGE_EMBEDDING_COL);
536
+ await this.ensureColumn(safe, "agent", "TEXT NOT NULL DEFAULT ''");
537
+ await this.ensureLookupIndex(safe, "path_creation_date", `("path", "creation_date")`);
538
+ }
539
+ /**
540
+ * Create the skills table.
541
+ *
542
+ * One row per skill version. Workers INSERT a fresh row on every KEEP /
543
+ * MERGE rather than UPDATE-ing in place, so the full version history is
544
+ * recoverable. Uniqueness in the *current* state is by (project_key, name)
545
+ * — newer rows shadow older ones at read time (ORDER BY version DESC).
546
+ * This sidesteps the Deeplake UPDATE-coalescing quirk that bit the wiki
547
+ * worker.
548
+ */
549
+ async ensureSkillsTable(name) {
550
+ const safe = sqlIdent(name);
519
551
  const tables = await this.listTables();
520
- if (!tables.includes(name)) {
521
- log2(`table "${name}" not found, creating`);
522
- await this.createTableWithRetry(`CREATE TABLE IF NOT EXISTS "${name}" (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`, name);
523
- log2(`table "${name}" created`);
524
- if (!tables.includes(name))
525
- this._tablesCache = [...tables, name];
526
- }
527
- await this.ensureEmbeddingColumn(name, MESSAGE_EMBEDDING_COL);
528
- await this.ensureColumn(name, "agent", "TEXT NOT NULL DEFAULT ''");
529
- await this.ensureLookupIndex(name, "path_creation_date", `("path", "creation_date")`);
552
+ if (!tables.includes(safe)) {
553
+ log2(`table "${safe}" not found, creating`);
554
+ 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);
555
+ log2(`table "${safe}" created`);
556
+ if (!tables.includes(safe))
557
+ this._tablesCache = [...tables, safe];
558
+ }
559
+ await this.ensureLookupIndex(safe, "project_key_name", `("project_key", "name")`);
530
560
  }
531
561
  };
532
562
 
@@ -120,6 +120,7 @@ function loadConfig() {
120
120
  apiUrl: process.env.HIVEMIND_API_URL ?? creds?.apiUrl ?? "https://api.deeplake.ai",
121
121
  tableName: process.env.HIVEMIND_TABLE ?? "memory",
122
122
  sessionsTableName: process.env.HIVEMIND_SESSIONS_TABLE ?? "sessions",
123
+ skillsTableName: process.env.HIVEMIND_SKILLS_TABLE ?? "skills",
123
124
  memoryPath: process.env.HIVEMIND_MEMORY_PATH ?? join2(home, ".deeplake", "memory")
124
125
  };
125
126
  }
@@ -147,6 +148,12 @@ function log(tag, msg) {
147
148
  function sqlStr(value) {
148
149
  return value.replace(/\\/g, "\\\\").replace(/'/g, "''").replace(/\0/g, "").replace(/[\x01-\x08\x0b\x0c\x0e-\x1f\x7f]/g, "");
149
150
  }
151
+ function sqlIdent(name) {
152
+ if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name)) {
153
+ throw new Error(`Invalid SQL identifier: ${JSON.stringify(name)}`);
154
+ }
155
+ return name;
156
+ }
150
157
 
151
158
  // dist/src/embeddings/columns.js
152
159
  var SUMMARY_EMBEDDING_COL = "summary_embedding";
@@ -510,7 +517,7 @@ var DeeplakeApi = class {
510
517
  }
511
518
  /** Create the memory table if it doesn't already exist. Migrate columns on existing tables. */
512
519
  async ensureTable(name) {
513
- const tbl = name ?? this.tableName;
520
+ const tbl = sqlIdent(name ?? this.tableName);
514
521
  const tables = await this.listTables();
515
522
  if (!tables.includes(tbl)) {
516
523
  log2(`table "${tbl}" not found, creating`);
@@ -524,17 +531,40 @@ var DeeplakeApi = class {
524
531
  }
525
532
  /** Create the sessions table (uses JSONB for message since every row is a JSON event). */
526
533
  async ensureSessionsTable(name) {
534
+ const safe = sqlIdent(name);
535
+ const tables = await this.listTables();
536
+ if (!tables.includes(safe)) {
537
+ log2(`table "${safe}" not found, creating`);
538
+ 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);
539
+ log2(`table "${safe}" created`);
540
+ if (!tables.includes(safe))
541
+ this._tablesCache = [...tables, safe];
542
+ }
543
+ await this.ensureEmbeddingColumn(safe, MESSAGE_EMBEDDING_COL);
544
+ await this.ensureColumn(safe, "agent", "TEXT NOT NULL DEFAULT ''");
545
+ await this.ensureLookupIndex(safe, "path_creation_date", `("path", "creation_date")`);
546
+ }
547
+ /**
548
+ * Create the skills table.
549
+ *
550
+ * One row per skill version. Workers INSERT a fresh row on every KEEP /
551
+ * MERGE rather than UPDATE-ing in place, so the full version history is
552
+ * recoverable. Uniqueness in the *current* state is by (project_key, name)
553
+ * — newer rows shadow older ones at read time (ORDER BY version DESC).
554
+ * This sidesteps the Deeplake UPDATE-coalescing quirk that bit the wiki
555
+ * worker.
556
+ */
557
+ async ensureSkillsTable(name) {
558
+ const safe = sqlIdent(name);
527
559
  const tables = await this.listTables();
528
- if (!tables.includes(name)) {
529
- log2(`table "${name}" not found, creating`);
530
- await this.createTableWithRetry(`CREATE TABLE IF NOT EXISTS "${name}" (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`, name);
531
- log2(`table "${name}" created`);
532
- if (!tables.includes(name))
533
- this._tablesCache = [...tables, name];
560
+ if (!tables.includes(safe)) {
561
+ log2(`table "${safe}" not found, creating`);
562
+ 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);
563
+ log2(`table "${safe}" created`);
564
+ if (!tables.includes(safe))
565
+ this._tablesCache = [...tables, safe];
534
566
  }
535
- await this.ensureEmbeddingColumn(name, MESSAGE_EMBEDDING_COL);
536
- await this.ensureColumn(name, "agent", "TEXT NOT NULL DEFAULT ''");
537
- await this.ensureLookupIndex(name, "path_creation_date", `("path", "creation_date")`);
567
+ await this.ensureLookupIndex(safe, "project_key_name", `("project_key", "name")`);
538
568
  }
539
569
  };
540
570
 
@@ -101,7 +101,6 @@ function getInstalledVersion(bundleDir, pluginManifestDir) {
101
101
  // dist/src/hooks/codex/session-start.js
102
102
  var log2 = (msg) => log("codex-session-start", msg);
103
103
  var __bundleDir = dirname2(fileURLToPath(import.meta.url));
104
- var AUTH_CMD = join4(__bundleDir, "commands", "auth-login.js");
105
104
  var context = `DEEPLAKE MEMORY: Persistent memory at ~/.deeplake/memory/ shared across sessions, users, and agents.
106
105
 
107
106
  Deeplake memory has THREE tiers \u2014 pick the right one for the question:
@@ -118,7 +117,32 @@ Search workflow:
118
117
  \u274C grep without a summaries/ or sessions/ suffix \u2014 too noisy
119
118
 
120
119
  IMPORTANT: Only use bash builtins (cat, ls, grep, echo, jq, head, tail, sed, awk, etc.) on ~/.deeplake/memory/. Do NOT use python, python3, node, curl, or other interpreters \u2014 they are not available in the memory filesystem.
121
- Do NOT spawn subagents to read deeplake memory.`;
120
+ Do NOT spawn subagents to read deeplake memory.
121
+
122
+ Organization management \u2014 each argument is SEPARATE (do NOT quote subcommands together):
123
+ - hivemind login \u2014 SSO login
124
+ - hivemind whoami \u2014 show current user/org
125
+ - hivemind org list \u2014 list organizations
126
+ - hivemind org switch <name-or-id> \u2014 switch organization
127
+ - hivemind workspaces \u2014 list workspaces
128
+ - hivemind workspace <id> \u2014 switch workspace
129
+ - hivemind invite <email> <ADMIN|WRITE|READ> \u2014 invite member (ALWAYS ask user which role before inviting)
130
+ - hivemind members \u2014 list members
131
+ - hivemind remove <user-id> \u2014 remove member
132
+
133
+ SKILLS (skilify) \u2014 mine + share reusable skills across the org:
134
+ - hivemind skilify \u2014 show scope/team/install + per-project state
135
+ - hivemind skilify pull \u2014 sync project skills from the org table
136
+ - hivemind skilify pull --user <email> \u2014 only that author's skills
137
+ - hivemind skilify pull --users a,b,c \u2014 multiple authors (CSV)
138
+ - hivemind skilify pull --all-users \u2014 explicit "no author filter"
139
+ - hivemind skilify pull --to project|global \u2014 install location
140
+ - hivemind skilify pull --dry-run \u2014 preview only
141
+ - hivemind skilify pull --force \u2014 overwrite local (creates .bak)
142
+ - hivemind skilify pull <skill-name> \u2014 pull only that skill (combines with --user)
143
+ - hivemind skilify scope <me|team|org> \u2014 sharing scope for new skills
144
+ - hivemind skilify install <project|global> \u2014 default install location
145
+ - hivemind skilify team add|remove|list <name> \u2014 manage team list`;
122
146
  async function main() {
123
147
  if (process.env.HIVEMIND_WIKI_WORKER === "1")
124
148
  return;
@@ -149,7 +173,7 @@ Hivemind v${current}`;
149
173
  }
150
174
  const additionalContext = creds?.token ? `${context}
151
175
  Logged in to Deeplake as org: ${creds.orgName ?? creds.orgId} (workspace: ${creds.workspaceId ?? "default"})${versionNotice}` : `${context}
152
- Not logged in to Deeplake. Run: node "${AUTH_CMD}" login${versionNotice}`;
176
+ Not logged in to Deeplake. Run: hivemind login${versionNotice}`;
153
177
  console.log(additionalContext);
154
178
  }
155
179
  main().catch((e) => {
@@ -66791,6 +66791,7 @@ function loadConfig() {
66791
66791
  apiUrl: process.env.HIVEMIND_API_URL ?? creds?.apiUrl ?? "https://api.deeplake.ai",
66792
66792
  tableName: process.env.HIVEMIND_TABLE ?? "memory",
66793
66793
  sessionsTableName: process.env.HIVEMIND_SESSIONS_TABLE ?? "sessions",
66794
+ skillsTableName: process.env.HIVEMIND_SKILLS_TABLE ?? "skills",
66794
66795
  memoryPath: process.env.HIVEMIND_MEMORY_PATH ?? join4(home, ".deeplake", "memory")
66795
66796
  };
66796
66797
  }
@@ -66818,6 +66819,12 @@ function sqlStr(value) {
66818
66819
  function sqlLike(value) {
66819
66820
  return sqlStr(value).replace(/%/g, "\\%").replace(/_/g, "\\_");
66820
66821
  }
66822
+ function sqlIdent(name) {
66823
+ if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name)) {
66824
+ throw new Error(`Invalid SQL identifier: ${JSON.stringify(name)}`);
66825
+ }
66826
+ return name;
66827
+ }
66821
66828
 
66822
66829
  // dist/src/embeddings/columns.js
66823
66830
  var SUMMARY_EMBEDDING_COL = "summary_embedding";
@@ -67190,7 +67197,7 @@ var DeeplakeApi = class {
67190
67197
  }
67191
67198
  /** Create the memory table if it doesn't already exist. Migrate columns on existing tables. */
67192
67199
  async ensureTable(name) {
67193
- const tbl = name ?? this.tableName;
67200
+ const tbl = sqlIdent(name ?? this.tableName);
67194
67201
  const tables = await this.listTables();
67195
67202
  if (!tables.includes(tbl)) {
67196
67203
  log2(`table "${tbl}" not found, creating`);
@@ -67204,17 +67211,40 @@ var DeeplakeApi = class {
67204
67211
  }
67205
67212
  /** Create the sessions table (uses JSONB for message since every row is a JSON event). */
67206
67213
  async ensureSessionsTable(name) {
67214
+ const safe = sqlIdent(name);
67215
+ const tables = await this.listTables();
67216
+ if (!tables.includes(safe)) {
67217
+ 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
+ log2(`table "${safe}" created`);
67220
+ if (!tables.includes(safe))
67221
+ this._tablesCache = [...tables, safe];
67222
+ }
67223
+ await this.ensureEmbeddingColumn(safe, MESSAGE_EMBEDDING_COL);
67224
+ await this.ensureColumn(safe, "agent", "TEXT NOT NULL DEFAULT ''");
67225
+ await this.ensureLookupIndex(safe, "path_creation_date", `("path", "creation_date")`);
67226
+ }
67227
+ /**
67228
+ * Create the skills table.
67229
+ *
67230
+ * One row per skill version. Workers INSERT a fresh row on every KEEP /
67231
+ * MERGE rather than UPDATE-ing in place, so the full version history is
67232
+ * recoverable. Uniqueness in the *current* state is by (project_key, name)
67233
+ * — newer rows shadow older ones at read time (ORDER BY version DESC).
67234
+ * This sidesteps the Deeplake UPDATE-coalescing quirk that bit the wiki
67235
+ * worker.
67236
+ */
67237
+ async ensureSkillsTable(name) {
67238
+ const safe = sqlIdent(name);
67207
67239
  const tables = await this.listTables();
67208
- if (!tables.includes(name)) {
67209
- log2(`table "${name}" not found, creating`);
67210
- await this.createTableWithRetry(`CREATE TABLE IF NOT EXISTS "${name}" (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`, name);
67211
- log2(`table "${name}" created`);
67212
- if (!tables.includes(name))
67213
- this._tablesCache = [...tables, name];
67214
- }
67215
- await this.ensureEmbeddingColumn(name, MESSAGE_EMBEDDING_COL);
67216
- await this.ensureColumn(name, "agent", "TEXT NOT NULL DEFAULT ''");
67217
- await this.ensureLookupIndex(name, "path_creation_date", `("path", "creation_date")`);
67240
+ if (!tables.includes(safe)) {
67241
+ log2(`table "${safe}" not found, creating`);
67242
+ 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);
67243
+ log2(`table "${safe}" created`);
67244
+ if (!tables.includes(safe))
67245
+ this._tablesCache = [...tables, safe];
67246
+ }
67247
+ await this.ensureLookupIndex(safe, "project_key_name", `("project_key", "name")`);
67218
67248
  }
67219
67249
  };
67220
67250