@deeplake/hivemind 0.7.26 → 0.7.28

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 (44) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/bundle/cli.js +1086 -93
  4. package/codex/bundle/capture.js +10 -5
  5. package/codex/bundle/commands/auth-login.js +10 -5
  6. package/codex/bundle/embeddings/embed-daemon.js +4 -2
  7. package/codex/bundle/pre-tool-use.js +10 -5
  8. package/codex/bundle/session-start-setup.js +10 -5
  9. package/codex/bundle/session-start.js +233 -136
  10. package/codex/bundle/shell/deeplake-shell.js +10 -5
  11. package/codex/bundle/skillify-worker.js +60 -21
  12. package/codex/bundle/stop.js +66 -25
  13. package/codex/bundle/wiki-worker.js +4 -2
  14. package/codex/skills/deeplake-memory/SKILL.md +33 -0
  15. package/cursor/bundle/capture.js +63 -22
  16. package/cursor/bundle/commands/auth-login.js +10 -5
  17. package/cursor/bundle/embeddings/embed-daemon.js +4 -2
  18. package/cursor/bundle/pre-tool-use.js +10 -5
  19. package/cursor/bundle/session-end.js +57 -19
  20. package/cursor/bundle/session-start.js +238 -87
  21. package/cursor/bundle/shell/deeplake-shell.js +10 -5
  22. package/cursor/bundle/skillify-worker.js +60 -21
  23. package/cursor/bundle/wiki-worker.js +4 -2
  24. package/hermes/bundle/capture.js +63 -22
  25. package/hermes/bundle/commands/auth-login.js +10 -5
  26. package/hermes/bundle/embeddings/embed-daemon.js +4 -2
  27. package/hermes/bundle/pre-tool-use.js +10 -5
  28. package/hermes/bundle/session-end.js +57 -19
  29. package/hermes/bundle/session-start.js +239 -87
  30. package/hermes/bundle/shell/deeplake-shell.js +10 -5
  31. package/hermes/bundle/skillify-worker.js +60 -21
  32. package/hermes/bundle/wiki-worker.js +4 -2
  33. package/mcp/bundle/server.js +10 -5
  34. package/openclaw/dist/chunks/{auth-creds-AEKS6D3P.js → auth-creds-KKTYIP27.js} +2 -1
  35. package/openclaw/dist/chunks/{chunk-SRCBBT4H.js → chunk-OSD5GJJ5.js} +2 -0
  36. package/openclaw/dist/chunks/{config-ZLH6JFJS.js → config-XEK4MJJS.js} +2 -0
  37. package/openclaw/dist/chunks/{index-marker-store-PGT5CW6T.js → index-marker-store-CPGF2BI7.js} +4 -2
  38. package/openclaw/dist/chunks/{setup-config-C35UK4LP.js → setup-config-VI54GEUM.js} +2 -0
  39. package/openclaw/dist/index.js +68 -19
  40. package/openclaw/dist/skillify-worker.js +67 -27
  41. package/openclaw/openclaw.plugin.json +1 -1
  42. package/openclaw/package.json +1 -1
  43. package/package.json +1 -1
  44. package/pi/extension-source/hivemind.ts +157 -19
package/bundle/cli.js CHANGED
@@ -500,6 +500,7 @@ function installOpenclaw() {
500
500
  throw new Error(`OpenClaw bundle missing at ${srcDist}. Run 'npm run build' first.`);
501
501
  }
502
502
  ensureDir(PLUGIN_DIR2);
503
+ rmSync(join5(PLUGIN_DIR2, "dist"), { recursive: true, force: true });
503
504
  copyDir(srcDist, join5(PLUGIN_DIR2, "dist"));
504
505
  if (existsSync4(srcManifest))
505
506
  copyFileSync(srcManifest, join5(PLUGIN_DIR2, "openclaw.plugin.json"));
@@ -4038,10 +4039,12 @@ import { randomUUID } from "node:crypto";
4038
4039
  import { appendFileSync } from "node:fs";
4039
4040
  import { join as join14 } from "node:path";
4040
4041
  import { homedir as homedir5 } from "node:os";
4041
- var DEBUG = process.env.HIVEMIND_DEBUG === "1";
4042
4042
  var LOG = join14(homedir5(), ".deeplake", "hook-debug.log");
4043
+ function isDebug() {
4044
+ return process.env.HIVEMIND_DEBUG === "1";
4045
+ }
4043
4046
  function log2(tag, msg) {
4044
- if (!DEBUG)
4047
+ if (!isDebug())
4045
4048
  return;
4046
4049
  appendFileSync(LOG, `${(/* @__PURE__ */ new Date()).toISOString()} [${tag}] ${msg}
4047
4050
  `);
@@ -4087,7 +4090,9 @@ var RETRYABLE_CODES = /* @__PURE__ */ new Set([429, 500, 502, 503, 504]);
4087
4090
  var MAX_RETRIES = 3;
4088
4091
  var BASE_DELAY_MS = 500;
4089
4092
  var MAX_CONCURRENCY = 5;
4090
- var QUERY_TIMEOUT_MS = Number(process.env.HIVEMIND_QUERY_TIMEOUT_MS ?? 1e4);
4093
+ function getQueryTimeoutMs() {
4094
+ return Number(process.env.HIVEMIND_QUERY_TIMEOUT_MS ?? 1e4);
4095
+ }
4091
4096
  function sleep(ms) {
4092
4097
  return new Promise((resolve) => setTimeout(resolve, ms));
4093
4098
  }
@@ -4168,8 +4173,9 @@ var DeeplakeApi = class {
4168
4173
  let lastError;
4169
4174
  for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
4170
4175
  let resp;
4176
+ const timeoutMs = getQueryTimeoutMs();
4171
4177
  try {
4172
- const signal = AbortSignal.timeout(QUERY_TIMEOUT_MS);
4178
+ const signal = AbortSignal.timeout(timeoutMs);
4173
4179
  resp = await fetch(`${this.apiUrl}/workspaces/${this.workspaceId}/tables/query`, {
4174
4180
  method: "POST",
4175
4181
  headers: {
@@ -4183,7 +4189,7 @@ var DeeplakeApi = class {
4183
4189
  });
4184
4190
  } catch (e) {
4185
4191
  if (isTimeoutError(e)) {
4186
- lastError = new Error(`Query timeout after ${QUERY_TIMEOUT_MS}ms`);
4192
+ lastError = new Error(`Query timeout after ${timeoutMs}ms`);
4187
4193
  throw lastError;
4188
4194
  }
4189
4195
  lastError = e instanceof Error ? e : new Error(String(e));
@@ -4801,9 +4807,9 @@ if (process.argv[1] && process.argv[1].endsWith("auth-login.js")) {
4801
4807
  }
4802
4808
 
4803
4809
  // dist/src/commands/skillify.js
4804
- import { readdirSync as readdirSync4, existsSync as existsSync20, readFileSync as readFileSync14, mkdirSync as mkdirSync8, renameSync as renameSync4 } from "node:fs";
4805
- import { homedir as homedir13 } from "node:os";
4806
- import { dirname as dirname4, join as join23 } from "node:path";
4810
+ import { readdirSync as readdirSync5, existsSync as existsSync24, readFileSync as readFileSync17, mkdirSync as mkdirSync10, renameSync as renameSync4 } from "node:fs";
4811
+ import { homedir as homedir17 } from "node:os";
4812
+ import { dirname as dirname6, join as join27 } from "node:path";
4807
4813
 
4808
4814
  // dist/src/skillify/scope-config.js
4809
4815
  import { existsSync as existsSync14, mkdirSync as mkdirSync4, readFileSync as readFileSync10, writeFileSync as writeFileSync7 } from "node:fs";
@@ -4887,6 +4893,35 @@ function assertValidSkillName(name) {
4887
4893
  throw new Error(`invalid skill name: must be kebab-case (lowercase a-z, 0-9, hyphen): ${name}`);
4888
4894
  }
4889
4895
  }
4896
+ function skillDir(skillsRoot, name) {
4897
+ return join18(skillsRoot, name);
4898
+ }
4899
+ function skillPath(skillsRoot, name) {
4900
+ return join18(skillDir(skillsRoot, name), "SKILL.md");
4901
+ }
4902
+ function renderFrontmatter(fm) {
4903
+ const lines = ["---"];
4904
+ lines.push(`name: ${fm.name}`);
4905
+ lines.push(`description: ${JSON.stringify(fm.description)}`);
4906
+ if (fm.trigger)
4907
+ lines.push(`trigger: ${JSON.stringify(fm.trigger)}`);
4908
+ if (fm.author)
4909
+ lines.push(`author: ${fm.author}`);
4910
+ lines.push(`source_sessions:`);
4911
+ for (const s of fm.source_sessions)
4912
+ lines.push(` - ${s}`);
4913
+ if (fm.contributors && fm.contributors.length > 0) {
4914
+ lines.push(`contributors:`);
4915
+ for (const c of fm.contributors)
4916
+ lines.push(` - ${c}`);
4917
+ }
4918
+ lines.push(`version: ${fm.version}`);
4919
+ lines.push(`created_by_agent: ${fm.created_by_agent}`);
4920
+ lines.push(`created_at: ${fm.created_at}`);
4921
+ lines.push(`updated_at: ${fm.updated_at}`);
4922
+ lines.push("---");
4923
+ return lines.join("\n");
4924
+ }
4890
4925
  function parseFrontmatter(text) {
4891
4926
  if (!text.startsWith("---\n") && !text.startsWith("---\r\n"))
4892
4927
  return null;
@@ -4936,6 +4971,62 @@ function parseFrontmatter(text) {
4936
4971
  }
4937
4972
  return { fm, body };
4938
4973
  }
4974
+ function writeNewSkill(args) {
4975
+ assertValidSkillName(args.name);
4976
+ const dir = skillDir(args.skillsRoot, args.name);
4977
+ const path = skillPath(args.skillsRoot, args.name);
4978
+ if (existsSync15(path)) {
4979
+ throw new Error(`skill already exists at ${path}; use mergeSkill`);
4980
+ }
4981
+ mkdirSync5(dir, { recursive: true });
4982
+ const now = (/* @__PURE__ */ new Date()).toISOString();
4983
+ const author = args.author && args.author.length > 0 ? args.author : void 0;
4984
+ const contributors = author ? [author] : [];
4985
+ const fm = {
4986
+ name: args.name,
4987
+ description: args.description,
4988
+ trigger: args.trigger,
4989
+ author,
4990
+ source_sessions: args.sourceSessions,
4991
+ contributors,
4992
+ version: 1,
4993
+ created_by_agent: args.agent,
4994
+ created_at: now,
4995
+ updated_at: now
4996
+ };
4997
+ const text = `${renderFrontmatter(fm)}
4998
+
4999
+ ${args.body.trim()}
5000
+ `;
5001
+ writeFileSync8(path, text);
5002
+ return {
5003
+ path,
5004
+ action: "created",
5005
+ version: 1,
5006
+ createdAt: now,
5007
+ updatedAt: now,
5008
+ author,
5009
+ contributors
5010
+ };
5011
+ }
5012
+ function listSkills(skillsRoot) {
5013
+ if (!existsSync15(skillsRoot))
5014
+ return [];
5015
+ const out = [];
5016
+ for (const name of readdirSync2(skillsRoot)) {
5017
+ const skillFile = join18(skillsRoot, name, "SKILL.md");
5018
+ if (existsSync15(skillFile) && statSync2(skillFile).isFile()) {
5019
+ out.push({ name, body: readFileSync11(skillFile, "utf-8") });
5020
+ }
5021
+ }
5022
+ return out;
5023
+ }
5024
+ function resolveSkillsRoot(install, cwd) {
5025
+ if (install === "global") {
5026
+ return join18(homedir8(), ".claude", "skills");
5027
+ }
5028
+ return join18(cwd, ".claude", "skills");
5029
+ }
4939
5030
 
4940
5031
  // dist/src/skillify/manifest.js
4941
5032
  import { existsSync as existsSync16, lstatSync as lstatSync3, mkdirSync as mkdirSync6, readFileSync as readFileSync12, renameSync as renameSync2, unlinkSync as unlinkSync7, writeFileSync as writeFileSync9 } from "node:fs";
@@ -5228,7 +5319,7 @@ function renderSkillFile(row) {
5228
5319
  updated_at: String(row.updated_at ?? (/* @__PURE__ */ new Date()).toISOString())
5229
5320
  };
5230
5321
  const body = String(row.body ?? "").trim();
5231
- return `${renderFrontmatter(fm)}
5322
+ return `${renderFrontmatter2(fm)}
5232
5323
 
5233
5324
  ${body}
5234
5325
  `;
@@ -5259,7 +5350,7 @@ function parseContributors(v) {
5259
5350
  }
5260
5351
  return [];
5261
5352
  }
5262
- function renderFrontmatter(fm) {
5353
+ function renderFrontmatter2(fm) {
5263
5354
  const lines = ["---"];
5264
5355
  lines.push(`name: ${fm.name}`);
5265
5356
  lines.push(`description: ${JSON.stringify(fm.description)}`);
@@ -5381,8 +5472,8 @@ async function runPull(opts) {
5381
5472
  summary.skipped++;
5382
5473
  continue;
5383
5474
  }
5384
- const skillDir = join21(root, dirName);
5385
- const skillFile = join21(skillDir, "SKILL.md");
5475
+ const skillDir2 = join21(root, dirName);
5476
+ const skillFile = join21(skillDir2, "SKILL.md");
5386
5477
  const remoteVersion = Number(row.version ?? 1);
5387
5478
  const localVersion = readLocalVersion(skillFile);
5388
5479
  const action = decideAction({
@@ -5393,7 +5484,7 @@ async function runPull(opts) {
5393
5484
  });
5394
5485
  let manifestError;
5395
5486
  if (action === "wrote") {
5396
- mkdirSync7(skillDir, { recursive: true });
5487
+ mkdirSync7(skillDir2, { recursive: true });
5397
5488
  if (existsSync18(skillFile)) {
5398
5489
  try {
5399
5490
  renameSync3(skillFile, `${skillFile}.bak`);
@@ -5401,7 +5492,7 @@ async function runPull(opts) {
5401
5492
  }
5402
5493
  }
5403
5494
  writeFileSync10(skillFile, renderSkillFile(row));
5404
- const symlinks = opts.install === "global" ? fanOutSymlinks(skillDir, dirName, detectAgentSkillsRoots(root)) : [];
5495
+ const symlinks = opts.install === "global" ? fanOutSymlinks(skillDir2, dirName, detectAgentSkillsRoots(root)) : [];
5405
5496
  try {
5406
5497
  recordPull({
5407
5498
  dirName,
@@ -5609,9 +5700,949 @@ function decideTargetForManifestEntry(entry, opts, userFilter, haveUserFilter) {
5609
5700
  return { shouldRemove: true };
5610
5701
  }
5611
5702
 
5703
+ // dist/src/commands/mine-local.js
5704
+ import { spawn } from "node:child_process";
5705
+ import { existsSync as existsSync23, mkdirSync as mkdirSync9, readFileSync as readFileSync16, writeFileSync as writeFileSync12 } from "node:fs";
5706
+ import { homedir as homedir16 } from "node:os";
5707
+ import { basename, dirname as dirname5, join as join26 } from "node:path";
5708
+
5709
+ // dist/src/skillify/local-source.js
5710
+ import { readdirSync as readdirSync4, readFileSync as readFileSync14, existsSync as existsSync20, statSync as statSync4 } from "node:fs";
5711
+ import { homedir as homedir13 } from "node:os";
5712
+ import { join as join23 } from "node:path";
5713
+ var HOME2 = homedir13();
5714
+ function encodeCwdClaudeCode(cwd) {
5715
+ return cwd.replace(/[/_]/g, "-");
5716
+ }
5717
+ function detectInstalledAgents() {
5718
+ const installs = [];
5719
+ const claudeRoot = join23(HOME2, ".claude", "projects");
5720
+ if (existsSync20(claudeRoot)) {
5721
+ installs.push({
5722
+ agent: "claude_code",
5723
+ sessionRoot: claudeRoot,
5724
+ encodeCwd: encodeCwdClaudeCode
5725
+ });
5726
+ }
5727
+ const codexRoot = join23(HOME2, ".codex", "sessions");
5728
+ if (existsSync20(codexRoot)) {
5729
+ installs.push({
5730
+ agent: "codex",
5731
+ sessionRoot: codexRoot,
5732
+ encodeCwd: () => "__cwd_unknown__"
5733
+ });
5734
+ }
5735
+ return installs;
5736
+ }
5737
+ function detectHostAgent() {
5738
+ if (process.env.CLAUDECODE === "1" || process.env.CLAUDE_CODE_ENTRYPOINT)
5739
+ return "claude_code";
5740
+ if (process.env.CODEX_HOME || process.env.CODEX_SESSION_ID)
5741
+ return "codex";
5742
+ return null;
5743
+ }
5744
+ function listLocalSessions(installs, cwd) {
5745
+ const out = [];
5746
+ for (const install of installs) {
5747
+ const cwdEncoded = install.encodeCwd(cwd);
5748
+ let subdirs = [];
5749
+ try {
5750
+ subdirs = readdirSync4(install.sessionRoot);
5751
+ } catch {
5752
+ continue;
5753
+ }
5754
+ for (const sub of subdirs) {
5755
+ const subdirPath = join23(install.sessionRoot, sub);
5756
+ try {
5757
+ if (!statSync4(subdirPath).isDirectory())
5758
+ continue;
5759
+ } catch {
5760
+ continue;
5761
+ }
5762
+ const inCwd = sub === cwdEncoded;
5763
+ let files = [];
5764
+ try {
5765
+ files = readdirSync4(subdirPath);
5766
+ } catch {
5767
+ continue;
5768
+ }
5769
+ for (const f of files) {
5770
+ if (!f.endsWith(".jsonl"))
5771
+ continue;
5772
+ const fullPath = join23(subdirPath, f);
5773
+ let stats;
5774
+ try {
5775
+ stats = statSync4(fullPath);
5776
+ } catch {
5777
+ continue;
5778
+ }
5779
+ if (!stats.isFile())
5780
+ continue;
5781
+ const sessionId = f.replace(/\.jsonl$/, "");
5782
+ out.push({
5783
+ agent: install.agent,
5784
+ path: fullPath,
5785
+ mtime: stats.mtimeMs,
5786
+ inCwd,
5787
+ sessionId
5788
+ });
5789
+ }
5790
+ }
5791
+ }
5792
+ return out;
5793
+ }
5794
+ function pickSessions(candidates, opts) {
5795
+ const { n, epsilon } = opts;
5796
+ if (n <= 0 || candidates.length === 0)
5797
+ return [];
5798
+ const sorted = [...candidates].sort((a, b) => b.mtime - a.mtime);
5799
+ const cwdQuota = Math.ceil((1 - epsilon) * n);
5800
+ const globalQuota = Math.floor(epsilon * n);
5801
+ const picked = [];
5802
+ const taken = /* @__PURE__ */ new Set();
5803
+ for (const s of sorted) {
5804
+ if (picked.length >= cwdQuota)
5805
+ break;
5806
+ if (s.inCwd && !taken.has(s.path)) {
5807
+ picked.push(s);
5808
+ taken.add(s.path);
5809
+ }
5810
+ }
5811
+ const cap2 = picked.length + globalQuota;
5812
+ for (const s of sorted) {
5813
+ if (picked.length >= cap2)
5814
+ break;
5815
+ if (!taken.has(s.path)) {
5816
+ picked.push(s);
5817
+ taken.add(s.path);
5818
+ }
5819
+ }
5820
+ for (const s of sorted) {
5821
+ if (picked.length >= n)
5822
+ break;
5823
+ if (!taken.has(s.path)) {
5824
+ picked.push(s);
5825
+ taken.add(s.path);
5826
+ }
5827
+ }
5828
+ return picked;
5829
+ }
5830
+ function nativeJsonlToRows(filePath, sessionId, agent) {
5831
+ let raw;
5832
+ try {
5833
+ raw = readFileSync14(filePath, "utf-8");
5834
+ } catch {
5835
+ return [];
5836
+ }
5837
+ const rows = [];
5838
+ let pendingAsstText;
5839
+ let pendingAsstTs;
5840
+ const flushAssistant = () => {
5841
+ if (pendingAsstText && pendingAsstText.trim().length > 0) {
5842
+ rows.push({
5843
+ type: "assistant_message",
5844
+ content: pendingAsstText,
5845
+ creation_date: pendingAsstTs,
5846
+ session_id: sessionId,
5847
+ agent
5848
+ });
5849
+ }
5850
+ pendingAsstText = void 0;
5851
+ pendingAsstTs = void 0;
5852
+ };
5853
+ for (const line of raw.split(/\n/)) {
5854
+ if (!line)
5855
+ continue;
5856
+ let obj;
5857
+ try {
5858
+ obj = JSON.parse(line);
5859
+ } catch {
5860
+ continue;
5861
+ }
5862
+ const t = obj?.type;
5863
+ const ts = obj?.timestamp ?? obj?.created_at;
5864
+ if (t === "user") {
5865
+ const c = obj?.message?.content;
5866
+ if (typeof c === "string" && c.trim().length > 0) {
5867
+ flushAssistant();
5868
+ rows.push({
5869
+ type: "user_message",
5870
+ content: c,
5871
+ creation_date: ts,
5872
+ session_id: sessionId,
5873
+ agent
5874
+ });
5875
+ }
5876
+ } else if (t === "assistant") {
5877
+ const c = obj?.message?.content;
5878
+ if (Array.isArray(c)) {
5879
+ const text = c.filter((b) => b?.type === "text" && typeof b.text === "string").map((b) => b.text).join("\n\n");
5880
+ if (text.trim().length > 0) {
5881
+ pendingAsstText = text;
5882
+ pendingAsstTs = ts;
5883
+ }
5884
+ }
5885
+ }
5886
+ }
5887
+ flushAssistant();
5888
+ return rows;
5889
+ }
5890
+
5891
+ // dist/src/skillify/extractors/index.js
5892
+ function extractPairs(rows) {
5893
+ const pairs2 = [];
5894
+ let pendingPrompt = null;
5895
+ let pendingAnswer = [];
5896
+ function flush() {
5897
+ if (pendingPrompt && pendingAnswer.length > 0) {
5898
+ pairs2.push({
5899
+ sessionId: pendingPrompt.row.session_id ?? "",
5900
+ agent: pendingPrompt.row.agent ?? null,
5901
+ date: pendingPrompt.row.creation_date ?? null,
5902
+ prompt: pendingPrompt.content,
5903
+ answer: pendingAnswer.join("\n\n")
5904
+ });
5905
+ }
5906
+ pendingPrompt = null;
5907
+ pendingAnswer = [];
5908
+ }
5909
+ for (const r of rows) {
5910
+ if (r.type === "user_message" && typeof r.content === "string") {
5911
+ flush();
5912
+ pendingPrompt = { content: r.content, row: r };
5913
+ } else if (r.type === "assistant_message" && typeof r.content === "string" && pendingPrompt) {
5914
+ if (r.content.trim().length > 0)
5915
+ pendingAnswer.push(r.content);
5916
+ }
5917
+ }
5918
+ flush();
5919
+ return pairs2;
5920
+ }
5921
+
5922
+ // dist/src/skillify/gate-runner.js
5923
+ import { existsSync as existsSync21 } from "node:fs";
5924
+ import { createRequire } from "node:module";
5925
+ import { homedir as homedir14 } from "node:os";
5926
+ import { join as join24 } from "node:path";
5927
+ var requireForCp = createRequire(import.meta.url);
5928
+ var { execFileSync: runChildProcess } = requireForCp("node:child_process");
5929
+ var inheritedEnv = process;
5930
+ function firstExistingPath(candidates) {
5931
+ for (const c of candidates) {
5932
+ if (existsSync21(c))
5933
+ return c;
5934
+ }
5935
+ return null;
5936
+ }
5937
+ function findAgentBin(agent) {
5938
+ const home = homedir14();
5939
+ switch (agent) {
5940
+ // /usr/bin/<name> is included in every candidate list — that's the
5941
+ // common Linux package-manager install path (apt, dnf, pacman). Old
5942
+ // code used `which` which always checked it; the static-scan fix
5943
+ // dropped `which`, so /usr/bin needs to be explicit. CodeRabbit on
5944
+ // #170 caught the gap.
5945
+ case "claude_code":
5946
+ return firstExistingPath([
5947
+ join24(home, ".claude", "local", "claude"),
5948
+ "/usr/local/bin/claude",
5949
+ "/usr/bin/claude",
5950
+ join24(home, ".npm-global", "bin", "claude"),
5951
+ join24(home, ".local", "bin", "claude"),
5952
+ "/opt/homebrew/bin/claude"
5953
+ ]) ?? join24(home, ".claude", "local", "claude");
5954
+ case "codex":
5955
+ return firstExistingPath([
5956
+ "/usr/local/bin/codex",
5957
+ "/usr/bin/codex",
5958
+ join24(home, ".npm-global", "bin", "codex"),
5959
+ join24(home, ".local", "bin", "codex"),
5960
+ "/opt/homebrew/bin/codex"
5961
+ ]) ?? "/usr/local/bin/codex";
5962
+ case "cursor":
5963
+ return firstExistingPath([
5964
+ "/usr/local/bin/cursor-agent",
5965
+ "/usr/bin/cursor-agent",
5966
+ join24(home, ".npm-global", "bin", "cursor-agent"),
5967
+ join24(home, ".local", "bin", "cursor-agent"),
5968
+ "/opt/homebrew/bin/cursor-agent"
5969
+ ]) ?? "/usr/local/bin/cursor-agent";
5970
+ case "hermes":
5971
+ return firstExistingPath([
5972
+ join24(home, ".local", "bin", "hermes"),
5973
+ "/usr/local/bin/hermes",
5974
+ "/usr/bin/hermes",
5975
+ join24(home, ".npm-global", "bin", "hermes"),
5976
+ "/opt/homebrew/bin/hermes"
5977
+ ]) ?? join24(home, ".local", "bin", "hermes");
5978
+ case "pi":
5979
+ return firstExistingPath([
5980
+ join24(home, ".local", "bin", "pi"),
5981
+ "/usr/local/bin/pi",
5982
+ "/usr/bin/pi",
5983
+ join24(home, ".npm-global", "bin", "pi"),
5984
+ "/opt/homebrew/bin/pi"
5985
+ ]) ?? join24(home, ".local", "bin", "pi");
5986
+ }
5987
+ }
5988
+
5989
+ // dist/src/skillify/gate-parser.js
5990
+ function extractJsonBlock(s) {
5991
+ const trimmed = s.trim();
5992
+ if (!trimmed)
5993
+ return null;
5994
+ const fenced = trimmed.match(/```(?:json)?\s*\n([\s\S]*?)\n```/);
5995
+ if (fenced)
5996
+ return fenced[1].trim();
5997
+ const start = trimmed.indexOf("{");
5998
+ if (start < 0)
5999
+ return null;
6000
+ let depth = 0;
6001
+ for (let i = start; i < trimmed.length; i++) {
6002
+ const c = trimmed[i];
6003
+ if (c === "{")
6004
+ depth++;
6005
+ else if (c === "}") {
6006
+ depth--;
6007
+ if (depth === 0)
6008
+ return trimmed.slice(start, i + 1);
6009
+ }
6010
+ }
6011
+ return null;
6012
+ }
6013
+
6014
+ // dist/src/skillify/local-manifest.js
6015
+ import { existsSync as existsSync22, mkdirSync as mkdirSync8, readFileSync as readFileSync15, writeFileSync as writeFileSync11 } from "node:fs";
6016
+ import { homedir as homedir15 } from "node:os";
6017
+ import { dirname as dirname4, join as join25 } from "node:path";
6018
+ var LOCAL_MANIFEST_PATH = join25(homedir15(), ".claude", "hivemind", "local-mined.json");
6019
+ var LOCAL_MINE_LOCK_PATH = join25(homedir15(), ".claude", "hivemind", "local-mined.lock");
6020
+ function readLocalManifest(path = LOCAL_MANIFEST_PATH) {
6021
+ if (!existsSync22(path))
6022
+ return null;
6023
+ try {
6024
+ return JSON.parse(readFileSync15(path, "utf-8"));
6025
+ } catch {
6026
+ return null;
6027
+ }
6028
+ }
6029
+ function writeLocalManifest(m, path = LOCAL_MANIFEST_PATH) {
6030
+ mkdirSync8(dirname4(path), { recursive: true });
6031
+ writeFileSync11(path, JSON.stringify(m, null, 2));
6032
+ }
6033
+
6034
+ // dist/src/commands/mine-local.js
6035
+ import { unlinkSync as unlinkSync9 } from "node:fs";
6036
+ var EPSILON = 0.3;
6037
+ var DEFAULT_N = 8;
6038
+ var PAIR_CHAR_CAP = 4e3;
6039
+ var PER_SESSION_PAIR_CAP = 30;
6040
+ var PER_SESSION_PROMPT_CAP = 12e4;
6041
+ var GATE_CONCURRENCY = 4;
6042
+ var IN_FLIGHT_MAX_AGE_MS = 6e4;
6043
+ var GATE_TIMEOUT_MS = 24e4;
6044
+ var MANIFEST_PATH = LOCAL_MANIFEST_PATH;
6045
+ function runGateViaStdin(opts) {
6046
+ return new Promise((resolve) => {
6047
+ if (opts.agent !== "claude_code") {
6048
+ resolve({
6049
+ stdout: "",
6050
+ stderr: "",
6051
+ errored: true,
6052
+ errorMessage: `stdin gate runner only supports claude_code (got ${opts.agent}); for other agents the prompt must fit in argv`
6053
+ });
6054
+ return;
6055
+ }
6056
+ if (!existsSync23(opts.bin)) {
6057
+ resolve({
6058
+ stdout: "",
6059
+ stderr: "",
6060
+ errored: true,
6061
+ errorMessage: `agent binary not found at ${opts.bin}`
6062
+ });
6063
+ return;
6064
+ }
6065
+ const args = [
6066
+ "-p",
6067
+ "--no-session-persistence",
6068
+ "--model",
6069
+ "haiku",
6070
+ "--permission-mode",
6071
+ "bypassPermissions"
6072
+ ];
6073
+ const child = spawn(opts.bin, args, {
6074
+ stdio: ["pipe", "pipe", "pipe"],
6075
+ env: { ...process.env, HIVEMIND_WIKI_WORKER: "1", HIVEMIND_CAPTURE: "false" }
6076
+ });
6077
+ let stdout = "";
6078
+ let stderr = "";
6079
+ let settled = false;
6080
+ const finish = (r) => {
6081
+ if (settled)
6082
+ return;
6083
+ settled = true;
6084
+ resolve(r);
6085
+ };
6086
+ const timer = setTimeout(() => {
6087
+ try {
6088
+ child.kill("SIGKILL");
6089
+ } catch {
6090
+ }
6091
+ finish({
6092
+ stdout,
6093
+ stderr,
6094
+ errored: true,
6095
+ errorMessage: `gate timed out after ${opts.timeoutMs}ms`
6096
+ });
6097
+ }, opts.timeoutMs);
6098
+ child.stdout.on("data", (b) => {
6099
+ stdout += b.toString("utf-8");
6100
+ });
6101
+ child.stderr.on("data", (b) => {
6102
+ stderr += b.toString("utf-8");
6103
+ });
6104
+ child.on("error", (e) => {
6105
+ clearTimeout(timer);
6106
+ finish({ stdout, stderr, errored: true, errorMessage: e.message });
6107
+ });
6108
+ child.on("close", (code) => {
6109
+ clearTimeout(timer);
6110
+ finish({
6111
+ stdout,
6112
+ stderr,
6113
+ errored: code !== 0,
6114
+ errorMessage: code !== 0 ? `claude_code CLI exited with code ${code}` : void 0
6115
+ });
6116
+ });
6117
+ child.stdin.on("error", (e) => {
6118
+ clearTimeout(timer);
6119
+ finish({ stdout, stderr, errored: true, errorMessage: `stdin write failed: ${e.message}` });
6120
+ });
6121
+ child.stdin.end(opts.prompt);
6122
+ });
6123
+ }
6124
+ var loadManifest2 = readLocalManifest;
6125
+ var saveManifest2 = writeLocalManifest;
6126
+ function truncate(s, max) {
6127
+ if (s.length <= max)
6128
+ return s;
6129
+ return s.slice(0, max) + `
6130
+ [\u2026truncated ${s.length - max} chars]`;
6131
+ }
6132
+ function renderPairsBlock(pairs2) {
6133
+ let total = 0;
6134
+ const out = [];
6135
+ for (const [i, p] of pairs2.entries()) {
6136
+ const block = `--- exchange ${i + 1} ---
6137
+ USER:
6138
+ ${truncate(p.prompt, PAIR_CHAR_CAP)}
6139
+
6140
+ ASSISTANT:
6141
+ ${truncate(p.answer, PAIR_CHAR_CAP)}
6142
+ `;
6143
+ if (total + block.length > PER_SESSION_PROMPT_CAP) {
6144
+ out.push(`[\u2026${pairs2.length - i} more exchanges omitted to stay under budget]`);
6145
+ break;
6146
+ }
6147
+ out.push(block);
6148
+ total += block.length;
6149
+ }
6150
+ return out.join("\n");
6151
+ }
6152
+ function buildSessionPrompt(pairs2, session, verdictPath) {
6153
+ return [
6154
+ `You are a skill curator examining ONE session of recent agent activity.`,
6155
+ `Your job: identify up to 3 distinct, non-overlapping reusable skills hiding in this session.`,
6156
+ `Distinct = different problem domains. Empty list is fine if nothing qualifies.`,
6157
+ ``,
6158
+ `Session: ${session.sessionId} (agent: ${session.agent})`,
6159
+ ``,
6160
+ `RULES:`,
6161
+ `- A skill qualifies if it captures a concrete, repeatable workflow OR a non-obvious`,
6162
+ ` constraint/gotcha a future engineer would benefit from knowing. Intra-session is fine \u2014`,
6163
+ ` one deep dive yielding a generalizable takeaway counts.`,
6164
+ `- Skip patterns that are obvious from reading the codebase or already in CLAUDE.md.`,
6165
+ `- Each body uses short sections (When to use, Workflow, Anti-patterns), concrete commands`,
6166
+ ` / paths / snippets drawn from the exchanges below, no marketing, no emojis.`,
6167
+ `- Each body under ~3000 characters.`,
6168
+ `- Skill names are kebab-case slugs (lowercase letters/digits/hyphens only).`,
6169
+ ``,
6170
+ `=== EXCHANGES (user prompts + assistant final answers, tool calls stripped) ===`,
6171
+ renderPairsBlock(pairs2),
6172
+ ``,
6173
+ `=== YOUR TASK ===`,
6174
+ `Output a single JSON object. You may either:`,
6175
+ ` (a) Write the JSON to this exact path using the Write tool: ${verdictPath}`,
6176
+ ` (b) Print the JSON object to stdout as your final message, nothing else.`,
6177
+ `Pick whichever you prefer. Do not do both.`,
6178
+ ``,
6179
+ `Required shape:`,
6180
+ `{`,
6181
+ ` "reason": "<one-line justification>",`,
6182
+ ` "skills": [`,
6183
+ ` {`,
6184
+ ` "name": "<kebab-case>",`,
6185
+ ` "description": "<one-line>",`,
6186
+ ` "trigger": "<short trigger>",`,
6187
+ ` "body": "<full SKILL.md body without frontmatter>"`,
6188
+ ` },`,
6189
+ ` ... up to 3 entries, or [] if nothing qualifies`,
6190
+ ` ]`,
6191
+ `}`,
6192
+ ``,
6193
+ `If you print to stdout, do not include any prose before or after the JSON.`
6194
+ ].join("\n");
6195
+ }
6196
+ function parseMultiVerdict(raw) {
6197
+ const block = extractJsonBlock(raw);
6198
+ if (!block)
6199
+ return null;
6200
+ let parsed;
6201
+ try {
6202
+ parsed = JSON.parse(block);
6203
+ } catch {
6204
+ return null;
6205
+ }
6206
+ if (!parsed || typeof parsed !== "object")
6207
+ return null;
6208
+ const skills = parsed.skills;
6209
+ if (!Array.isArray(skills))
6210
+ return null;
6211
+ const out = [];
6212
+ for (const s of skills) {
6213
+ if (!s || typeof s !== "object")
6214
+ continue;
6215
+ const name = typeof s.name === "string" ? s.name.trim() : "";
6216
+ const description = typeof s.description === "string" ? s.description.trim() : "";
6217
+ const body = typeof s.body === "string" ? s.body.trim() : "";
6218
+ const trigger = typeof s.trigger === "string" ? s.trigger.trim() : void 0;
6219
+ if (!name || !body)
6220
+ continue;
6221
+ out.push({ name, description, body, trigger });
6222
+ }
6223
+ return { reason: typeof parsed.reason === "string" ? parsed.reason : void 0, skills: out };
6224
+ }
6225
+ function gateAgentFor(host, fallback, installs) {
6226
+ const installed = new Set(installs.map((i) => i.agent));
6227
+ if (installed.has("claude_code"))
6228
+ return "claude_code";
6229
+ return host ?? fallback;
6230
+ }
6231
+ async function parallelMap(items, concurrency, fn) {
6232
+ const results = new Array(items.length);
6233
+ let cursor = 0;
6234
+ const workers = [];
6235
+ for (let w = 0; w < Math.min(concurrency, items.length); w++) {
6236
+ workers.push((async () => {
6237
+ while (true) {
6238
+ const i = cursor++;
6239
+ if (i >= items.length)
6240
+ return;
6241
+ results[i] = await fn(items[i], i);
6242
+ }
6243
+ })());
6244
+ }
6245
+ await Promise.all(workers);
6246
+ return results;
6247
+ }
6248
+ var SUMMARY_STOPWORDS = /* @__PURE__ */ new Set([
6249
+ "the",
6250
+ "and",
6251
+ "for",
6252
+ "with",
6253
+ "from",
6254
+ "into",
6255
+ "via",
6256
+ "this",
6257
+ "that",
6258
+ "your",
6259
+ "you",
6260
+ "are",
6261
+ "was",
6262
+ "were",
6263
+ "use",
6264
+ "using",
6265
+ "uses",
6266
+ "used",
6267
+ "skill",
6268
+ "when",
6269
+ "what",
6270
+ "where",
6271
+ "which",
6272
+ "while",
6273
+ "how",
6274
+ "non",
6275
+ "any",
6276
+ "all",
6277
+ "code",
6278
+ "file",
6279
+ "files",
6280
+ "way",
6281
+ "ways",
6282
+ "via"
6283
+ ]);
6284
+ function summaryTokens(s) {
6285
+ return new Set(s.toLowerCase().split(/[^a-z0-9]+/).filter((t) => t.length > 3 && !SUMMARY_STOPWORDS.has(t)));
6286
+ }
6287
+ function jaccard(a, b) {
6288
+ if (a.size === 0 || b.size === 0)
6289
+ return 0;
6290
+ let intersection = 0;
6291
+ for (const t of a)
6292
+ if (b.has(t))
6293
+ intersection++;
6294
+ return intersection / (a.size + b.size - intersection);
6295
+ }
6296
+ var OVERLAP_THRESHOLD = 0.4;
6297
+ function findOverlap(candidateDesc, others) {
6298
+ const ct = summaryTokens(candidateDesc);
6299
+ let best = null;
6300
+ for (const e of others) {
6301
+ const score = jaccard(ct, summaryTokens(e.desc));
6302
+ if (score >= OVERLAP_THRESHOLD && (!best || score > best.score)) {
6303
+ best = { name: e.name, score };
6304
+ }
6305
+ }
6306
+ return best;
6307
+ }
6308
+ function loadExistingSummaries(skillsRoot) {
6309
+ const out = [];
6310
+ for (const s of listSkills(skillsRoot)) {
6311
+ const parsed = parseFrontmatter(s.body);
6312
+ const desc = parsed?.fm.description ?? "";
6313
+ if (desc)
6314
+ out.push({ name: s.name, desc });
6315
+ }
6316
+ return out;
6317
+ }
6318
+ function takeFlagValue(args, flag) {
6319
+ const idx = args.indexOf(flag);
6320
+ if (idx < 0)
6321
+ return null;
6322
+ const v = args[idx + 1];
6323
+ if (v === void 0 || v.startsWith("--")) {
6324
+ console.error(`${flag} requires a value`);
6325
+ process.exit(1);
6326
+ }
6327
+ args.splice(idx, 2);
6328
+ return v;
6329
+ }
6330
+ function takeBoolFlag(args, flag) {
6331
+ const idx = args.indexOf(flag);
6332
+ if (idx < 0)
6333
+ return false;
6334
+ args.splice(idx, 1);
6335
+ return true;
6336
+ }
6337
+ async function runMineLocal(args) {
6338
+ let lockReleased = false;
6339
+ const releaseLock = () => {
6340
+ if (lockReleased)
6341
+ return;
6342
+ lockReleased = true;
6343
+ try {
6344
+ unlinkSync9(LOCAL_MINE_LOCK_PATH);
6345
+ } catch {
6346
+ }
6347
+ };
6348
+ process.on("exit", releaseLock);
6349
+ try {
6350
+ return await runMineLocalImpl(args);
6351
+ } finally {
6352
+ releaseLock();
6353
+ }
6354
+ }
6355
+ async function runMineLocalImpl(args) {
6356
+ const work = [...args];
6357
+ const force = takeBoolFlag(work, "--force");
6358
+ const dryRun = takeBoolFlag(work, "--dry-run");
6359
+ const nRaw = takeFlagValue(work, "--n");
6360
+ if (loadManifest2() && !force) {
6361
+ console.error(`Local skills have already been mined on this machine.`);
6362
+ console.error(`Manifest: ${MANIFEST_PATH}`);
6363
+ console.error(`Pass --force to re-mine.`);
6364
+ process.exit(1);
6365
+ }
6366
+ const installs = detectInstalledAgents();
6367
+ if (installs.length === 0) {
6368
+ console.error(`No agent session directories detected. Run a session first.`);
6369
+ process.exit(1);
6370
+ }
6371
+ console.log(`Detected installed agents: ${installs.map((i) => i.agent).join(", ")}`);
6372
+ const host = detectHostAgent();
6373
+ const fallback = installs[0].agent;
6374
+ const gateAgent = gateAgentFor(host, fallback, installs);
6375
+ if (gateAgent !== "claude_code") {
6376
+ console.error(`mine-local v1 requires the Claude Code CLI as its LLM gate.`);
6377
+ console.error(`Detected gate agent: ${gateAgent} (no claude_code session dir found at ~/.claude/projects/).`);
6378
+ console.error(`Install Claude Code, or run a Claude Code session once, then re-run.`);
6379
+ process.exit(1);
6380
+ }
6381
+ const gateBin = findAgentBin(gateAgent);
6382
+ console.log(`Gate CLI: ${gateAgent} (${gateBin})${host ? " \u2014 host-agent detected" : ""}`);
6383
+ const cwd = process.cwd();
6384
+ const rawSessions = listLocalSessions(installs, cwd);
6385
+ const now = Date.now();
6386
+ const allSessions = rawSessions.filter((s) => now - s.mtime >= IN_FLIGHT_MAX_AGE_MS);
6387
+ const dropped = rawSessions.length - allSessions.length;
6388
+ const cwdCount = allSessions.filter((s) => s.inCwd).length;
6389
+ console.log(`Found ${allSessions.length} local session(s) (${cwdCount} in cwd${dropped > 0 ? `, ${dropped} in-flight skipped` : ""})`);
6390
+ if (allSessions.length === 0) {
6391
+ console.error(`No mineable session files (all were modified within the last ${IN_FLIGHT_MAX_AGE_MS / 1e3}s).`);
6392
+ process.exit(1);
6393
+ }
6394
+ const n = nRaw === "all" ? allSessions.length : nRaw ? Math.max(1, parseInt(nRaw, 10) || DEFAULT_N) : DEFAULT_N;
6395
+ const picked = pickSessions(allSessions, { n, epsilon: EPSILON });
6396
+ console.log(`Picking ${picked.length} session(s) (\u03B5=${EPSILON}, N=${n}): ${picked.map((s) => s.sessionId.slice(0, 8)).join(", ")}`);
6397
+ if (dryRun) {
6398
+ console.log(`Dry-run: would invoke ${gateAgent} gate on ${picked.length} session(s) in parallel (concurrency=${GATE_CONCURRENCY}).`);
6399
+ return;
6400
+ }
6401
+ const tmpDir = join26(homedir16(), ".claude", "hivemind", `mine-local-${Date.now()}`);
6402
+ mkdirSync9(tmpDir, { recursive: true });
6403
+ console.log(`Running ${picked.length} gate call(s) in parallel (concurrency=${GATE_CONCURRENCY}, timeout=${GATE_TIMEOUT_MS / 1e3}s each)...`);
6404
+ const results = await parallelMap(picked, GATE_CONCURRENCY, async (s) => {
6405
+ const shortId = s.sessionId.slice(0, 8);
6406
+ const rows = nativeJsonlToRows(s.path, s.sessionId, s.agent);
6407
+ const pairs2 = extractPairs(rows);
6408
+ if (pairs2.length === 0) {
6409
+ console.log(` [${shortId}] no usable pairs \u2014 skipped`);
6410
+ return { session: s, skills: [], reason: "no pairs", error: null };
6411
+ }
6412
+ const tail = pairs2.slice(-PER_SESSION_PAIR_CAP);
6413
+ const sessionTmp = join26(tmpDir, `s-${shortId}`);
6414
+ mkdirSync9(sessionTmp, { recursive: true });
6415
+ const verdictPath = join26(sessionTmp, "verdict.json");
6416
+ const prompt = buildSessionPrompt(tail, s, verdictPath);
6417
+ writeFileSync12(join26(sessionTmp, "prompt.txt"), prompt);
6418
+ const gate = await runGateViaStdin({ agent: gateAgent, bin: gateBin, prompt, timeoutMs: GATE_TIMEOUT_MS });
6419
+ try {
6420
+ writeFileSync12(join26(sessionTmp, "gate-stdout.txt"), gate.stdout);
6421
+ if (gate.stderr)
6422
+ writeFileSync12(join26(sessionTmp, "gate-stderr.txt"), gate.stderr);
6423
+ } catch {
6424
+ }
6425
+ if (gate.errored) {
6426
+ console.log(` [${shortId}] gate failed: ${gate.errorMessage}`);
6427
+ return { session: s, skills: [], reason: null, error: gate.errorMessage ?? "gate failed" };
6428
+ }
6429
+ const verdictText = existsSync23(verdictPath) ? readFileSync16(verdictPath, "utf-8") : gate.stdout;
6430
+ const mv = parseMultiVerdict(verdictText);
6431
+ if (!mv) {
6432
+ console.log(` [${shortId}] unparseable verdict (kept at ${sessionTmp})`);
6433
+ return { session: s, skills: [], reason: null, error: "unparseable verdict" };
6434
+ }
6435
+ console.log(` [${shortId}] ${mv.skills.length} skill candidate(s) \u2014 ${mv.reason ?? "no reason given"}`);
6436
+ return { session: s, skills: mv.skills, reason: mv.reason ?? null, error: null };
6437
+ });
6438
+ const skillsRoot = resolveSkillsRoot("global", cwd);
6439
+ const totalCandidates = results.reduce((sum, r) => sum + r.skills.length, 0);
6440
+ const existingSummaries = loadExistingSummaries(skillsRoot);
6441
+ console.log("");
6442
+ console.log(`Got ${totalCandidates} candidate(s) across ${picked.length} session(s). Checking overlap against ${existingSummaries.length} installed skill(s) + each new write.`);
6443
+ if (totalCandidates === 0) {
6444
+ const existing = loadManifest2();
6445
+ saveManifest2({
6446
+ created_at: existing?.created_at ?? (/* @__PURE__ */ new Date()).toISOString(),
6447
+ entries: existing?.entries ?? []
6448
+ });
6449
+ console.log(`No skills to write.`);
6450
+ console.log(`tmp dir kept for inspection: ${tmpDir}`);
6451
+ return;
6452
+ }
6453
+ const flat = [];
6454
+ for (const r of results) {
6455
+ for (const sk of r.skills)
6456
+ flat.push({ skill: sk, session: r.session });
6457
+ }
6458
+ flat.sort((a, b) => b.session.mtime - a.session.mtime);
6459
+ const fanOutRoots = detectAgentSkillsRoots(skillsRoot);
6460
+ if (fanOutRoots.length > 0) {
6461
+ console.log(`Fan-out targets: ${fanOutRoots.join(", ")}`);
6462
+ }
6463
+ const written = [];
6464
+ const knownSummaries = [...existingSummaries];
6465
+ for (const { skill, session } of flat) {
6466
+ const overlap = findOverlap(skill.description, knownSummaries);
6467
+ if (overlap) {
6468
+ console.log(` skipped ${skill.name} \u2190 session ${session.sessionId.slice(0, 8)} (description overlaps "${overlap.name}", Jaccard=${overlap.score.toFixed(2)})`);
6469
+ continue;
6470
+ }
6471
+ try {
6472
+ const result = writeNewSkill({
6473
+ skillsRoot,
6474
+ name: skill.name,
6475
+ description: skill.description,
6476
+ trigger: skill.trigger,
6477
+ body: skill.body,
6478
+ sourceSessions: [session.sessionId],
6479
+ agent: gateAgent
6480
+ });
6481
+ const canonicalDir = dirname5(result.path);
6482
+ const symlinks = fanOutRoots.length > 0 ? fanOutSymlinks(canonicalDir, basename(canonicalDir), fanOutRoots) : [];
6483
+ const symlinkSuffix = symlinks.length > 0 ? `, fan-out \u2192 ${symlinks.length} root(s)` : "";
6484
+ console.log(` wrote ${skill.name} \u2190 session ${session.sessionId.slice(0, 8)} (${session.agent}${symlinkSuffix})`);
6485
+ written.push({ skill, session, result, symlinks });
6486
+ knownSummaries.push({ name: skill.name, desc: skill.description });
6487
+ } catch (e) {
6488
+ if (/already exists/i.test(e.message ?? "")) {
6489
+ console.log(` skipped ${skill.name} (file already exists at ${skillsRoot})`);
6490
+ } else {
6491
+ console.log(` failed ${skill.name}: ${e.message}`);
6492
+ }
6493
+ }
6494
+ }
6495
+ if (written.length > 0) {
6496
+ const existing = loadManifest2();
6497
+ const newEntries = written.map(({ skill, session, result, symlinks }) => ({
6498
+ skill_name: skill.name,
6499
+ canonical_path: result.path,
6500
+ symlinks,
6501
+ source_session_ids: [session.sessionId],
6502
+ source_session_paths: [session.path],
6503
+ source_agent: session.agent,
6504
+ gate_agent: gateAgent,
6505
+ created_at: result.createdAt,
6506
+ uploaded: false
6507
+ }));
6508
+ saveManifest2({
6509
+ created_at: existing?.created_at ?? (/* @__PURE__ */ new Date()).toISOString(),
6510
+ entries: [...existing?.entries ?? [], ...newEntries]
6511
+ });
6512
+ }
6513
+ console.log("");
6514
+ console.log(`Mined ${written.length} skill(s) from ${picked.length} session(s) (${results.filter((r) => r.skills.length > 0).length} session(s) contributed candidate(s)).`);
6515
+ console.log(`Installed to ${skillsRoot}/ \u2014 local-only, not shared.`);
6516
+ console.log(`Sign in with 'hivemind login' to share with your team later.`);
6517
+ }
6518
+
6519
+ // dist/src/cli/skillify-spec.js
6520
+ var SKILLIFY_SPEC = [
6521
+ {
6522
+ cmd: "hivemind skillify",
6523
+ desc: "show scope, team, install, per-project state"
6524
+ },
6525
+ {
6526
+ cmd: "hivemind skillify pull",
6527
+ desc: "sync project skills from the org table to local FS",
6528
+ options: [
6529
+ { flag: "--user <email>", desc: "only skills authored by that user" },
6530
+ { flag: "--users <a,b,c>", desc: "only skills from those authors" },
6531
+ { flag: "--all-users", desc: 'explicit "no author filter" (default)' },
6532
+ { flag: "--to <project|global>", desc: "install location (project=cwd/.claude/skills, global=~/.claude/skills)" },
6533
+ { flag: "--dry-run", desc: "preview without touching disk" },
6534
+ { flag: "--force", desc: "overwrite local files even if up-to-date (creates .bak)" },
6535
+ { flag: "<skill-name>", desc: "pull only that one skill (combines with --user)" }
6536
+ ],
6537
+ note: "every agent's SessionStart hook auto-runs 'pull --all-users --to global' on every session. File writes are idempotent (skipped when local is at-or-newer than remote). Disable via HIVEMIND_AUTOPULL_DISABLED=1."
6538
+ },
6539
+ {
6540
+ cmd: "hivemind skillify unpull",
6541
+ desc: "remove every skill previously installed by pull",
6542
+ options: [
6543
+ { flag: "--user <email>", desc: "remove only that author's pulls" },
6544
+ { flag: "--not-mine", desc: "remove all pulls except your own" },
6545
+ { flag: "--dry-run", desc: "preview without touching disk" }
6546
+ ]
6547
+ },
6548
+ {
6549
+ cmd: "hivemind skillify scope",
6550
+ args: "<me|team|org>",
6551
+ desc: "sharing scope for newly mined skills"
6552
+ },
6553
+ {
6554
+ cmd: "hivemind skillify install",
6555
+ args: "<project|global>",
6556
+ desc: "default install location for new skills"
6557
+ },
6558
+ {
6559
+ cmd: "hivemind skillify promote",
6560
+ args: "<skill-name>",
6561
+ desc: "move a project skill to the global location"
6562
+ },
6563
+ {
6564
+ cmd: "hivemind skillify team add|remove|list",
6565
+ args: "<name>",
6566
+ desc: "manage team member list"
6567
+ },
6568
+ {
6569
+ cmd: "hivemind skillify mine-local",
6570
+ desc: "one-shot: mine skills from local sessions (no auth needed)",
6571
+ options: [
6572
+ { flag: "--n <num|all>", desc: "how many sessions to mine (default: 8)" },
6573
+ { flag: "--force", desc: "re-run even if the manifest sentinel exists" },
6574
+ { flag: "--dry-run", desc: "stop before calling the LLM gate" }
6575
+ ]
6576
+ }
6577
+ ];
6578
+ function renderCliHelpBlock() {
6579
+ const INDENT = " ";
6580
+ const CMD_COL_WIDTH = 42;
6581
+ const lines = [];
6582
+ for (const sub of SKILLIFY_SPEC) {
6583
+ const left = sub.args ? `${sub.cmd} ${sub.args}` : sub.cmd;
6584
+ const padded = left.length >= CMD_COL_WIDTH ? `${left} ` : left.padEnd(CMD_COL_WIDTH);
6585
+ lines.push(`${INDENT}${padded}${capitalize(sub.desc)}.`);
6586
+ if (sub.options && sub.options.length > 0) {
6587
+ const optsList = sub.options.map((o) => o.flag).join(", ");
6588
+ lines.push(`${INDENT}${" ".repeat(CMD_COL_WIDTH)}Options: ${optsList}.`);
6589
+ }
6590
+ if (sub.note) {
6591
+ const noteWrapped = wrapAt(`Note: ${sub.note}`, 72);
6592
+ for (const noteLine of noteWrapped) {
6593
+ lines.push(`${INDENT}${" ".repeat(CMD_COL_WIDTH)}${noteLine}`);
6594
+ }
6595
+ }
6596
+ }
6597
+ return lines.join("\n");
6598
+ }
6599
+ function renderSubcommandUsageBlock() {
6600
+ const INDENT = " ";
6601
+ const SUB_INDENT = " ";
6602
+ const FLAG_INDENT = " ";
6603
+ const CMD_COL_WIDTH = 44;
6604
+ const FLAG_COL_WIDTH = 26;
6605
+ const lines = [];
6606
+ for (const sub of SKILLIFY_SPEC) {
6607
+ const left = sub.args ? `${sub.cmd} ${sub.args}` : sub.cmd;
6608
+ const padded = left.length >= CMD_COL_WIDTH ? `${left} ` : left.padEnd(CMD_COL_WIDTH);
6609
+ lines.push(`${INDENT}${padded}${sub.desc}`);
6610
+ if (sub.options && sub.options.length > 0) {
6611
+ const tail = sub.cmd.split(" ").slice(-1)[0];
6612
+ lines.push(`${SUB_INDENT}Options for ${tail}:`);
6613
+ for (const opt of sub.options) {
6614
+ const flagPadded = opt.flag.length >= FLAG_COL_WIDTH ? `${opt.flag} ` : opt.flag.padEnd(FLAG_COL_WIDTH);
6615
+ lines.push(`${FLAG_INDENT}${flagPadded}${opt.desc}`);
6616
+ }
6617
+ }
6618
+ }
6619
+ return lines.join("\n");
6620
+ }
6621
+ function capitalize(s) {
6622
+ return s.length === 0 ? s : s[0].toUpperCase() + s.slice(1);
6623
+ }
6624
+ function wrapAt(s, max) {
6625
+ const words = s.split(/\s+/);
6626
+ const out = [];
6627
+ let cur = "";
6628
+ for (const w of words) {
6629
+ if (cur.length === 0) {
6630
+ cur = w;
6631
+ } else if (cur.length + 1 + w.length > max) {
6632
+ out.push(cur);
6633
+ cur = w;
6634
+ } else {
6635
+ cur += " " + w;
6636
+ }
6637
+ }
6638
+ if (cur)
6639
+ out.push(cur);
6640
+ return out;
6641
+ }
6642
+
5612
6643
  // dist/src/commands/skillify.js
5613
6644
  function stateDir() {
5614
- return join23(homedir13(), ".deeplake", "state", "skillify");
6645
+ return join27(homedir17(), ".deeplake", "state", "skillify");
5615
6646
  }
5616
6647
  function showStatus() {
5617
6648
  const cfg = loadScopeConfig();
@@ -5619,11 +6650,11 @@ function showStatus() {
5619
6650
  console.log(`team: ${cfg.team.length === 0 ? "(empty)" : cfg.team.join(", ")}`);
5620
6651
  console.log(`install: ${cfg.install} (${cfg.install === "global" ? "~/.claude/skills/" : "<project>/.claude/skills/"})`);
5621
6652
  const dir = stateDir();
5622
- if (!existsSync20(dir)) {
6653
+ if (!existsSync24(dir)) {
5623
6654
  console.log(`state: (no projects tracked yet)`);
5624
6655
  return;
5625
6656
  }
5626
- const files = readdirSync4(dir).filter((f) => f.endsWith(".json") && f !== "config.json" && f !== "pulled.json" && f !== "autopull-last-run.json");
6657
+ const files = readdirSync5(dir).filter((f) => f.endsWith(".json") && f !== "config.json" && f !== "pulled.json" && f !== "autopull-last-run.json");
5627
6658
  if (files.length === 0) {
5628
6659
  console.log(`state: (no projects tracked yet)`);
5629
6660
  return;
@@ -5631,7 +6662,7 @@ function showStatus() {
5631
6662
  console.log(`state: ${files.length} project(s) tracked`);
5632
6663
  for (const f of files) {
5633
6664
  try {
5634
- const s = JSON.parse(readFileSync14(join23(dir, f), "utf-8"));
6665
+ const s = JSON.parse(readFileSync17(join27(dir, f), "utf-8"));
5635
6666
  const last = typeof s.updatedAt === "number" ? new Date(s.updatedAt).toISOString() : s.lastDate ?? "never";
5636
6667
  const skills = Array.isArray(s.skillsGenerated) && s.skillsGenerated.length > 0 ? s.skillsGenerated.join(", ") : "none";
5637
6668
  console.log(` - ${s.project} (counter=${s.counter}, last=${last}, skills=${skills})`);
@@ -5658,7 +6689,7 @@ function setInstall(loc) {
5658
6689
  }
5659
6690
  const cfg = loadScopeConfig();
5660
6691
  saveScopeConfig({ ...cfg, install: loc });
5661
- const path = loc === "global" ? join23(homedir13(), ".claude", "skills") : "<cwd>/.claude/skills";
6692
+ const path = loc === "global" ? join27(homedir17(), ".claude", "skills") : "<cwd>/.claude/skills";
5662
6693
  console.log(`Install location set to '${loc}'. New skills will be written to ${path}/<name>/SKILL.md.`);
5663
6694
  }
5664
6695
  function promoteSkill(name, cwd) {
@@ -5666,17 +6697,17 @@ function promoteSkill(name, cwd) {
5666
6697
  console.error("Usage: hivemind skillify promote <skill-name>");
5667
6698
  process.exit(1);
5668
6699
  }
5669
- const projectPath = join23(cwd, ".claude", "skills", name);
5670
- const globalPath = join23(homedir13(), ".claude", "skills", name);
5671
- if (!existsSync20(join23(projectPath, "SKILL.md"))) {
6700
+ const projectPath = join27(cwd, ".claude", "skills", name);
6701
+ const globalPath = join27(homedir17(), ".claude", "skills", name);
6702
+ if (!existsSync24(join27(projectPath, "SKILL.md"))) {
5672
6703
  console.error(`Skill '${name}' not found at ${projectPath}/SKILL.md`);
5673
6704
  process.exit(1);
5674
6705
  }
5675
- if (existsSync20(join23(globalPath, "SKILL.md"))) {
6706
+ if (existsSync24(join27(globalPath, "SKILL.md"))) {
5676
6707
  console.error(`Skill '${name}' already exists at ${globalPath}/SKILL.md \u2014 refusing to overwrite. Remove it first or rename the project skill.`);
5677
6708
  process.exit(1);
5678
6709
  }
5679
- mkdirSync8(dirname4(globalPath), { recursive: true });
6710
+ mkdirSync10(dirname6(globalPath), { recursive: true });
5680
6711
  renameSync4(projectPath, globalPath);
5681
6712
  console.log(`Promoted '${name}' from ${projectPath} \u2192 ${globalPath}.`);
5682
6713
  }
@@ -5719,33 +6750,9 @@ function teamList() {
5719
6750
  }
5720
6751
  function usage() {
5721
6752
  console.log("Usage:");
5722
- console.log(" hivemind skillify show current scope, team, install, and per-project state");
5723
- console.log(" hivemind skillify scope <me|team> set the mining scope");
5724
- console.log(" hivemind skillify install <project|global> set where new skills are written");
5725
- console.log(" hivemind skillify promote <skill-name> move a project skill to the global location");
5726
- console.log(" hivemind skillify team add <username> add a username to the team list");
5727
- console.log(" hivemind skillify team remove <username> remove a username from the team list");
5728
- console.log(" hivemind skillify team list list current team members");
5729
- console.log(" hivemind skillify pull [skill-name] [opts] fetch skills from Deeplake to local FS");
5730
- console.log(" Options for pull:");
5731
- console.log(" --to <project|global> destination (default: global)");
5732
- console.log(" --user <name> only skills authored by this user");
5733
- console.log(" --users <a,b,c> only skills authored by these users");
5734
- console.log(" --all-users all authors (default \u2014 equivalent to no filter)");
5735
- console.log(" --dry-run show what would be written, don't touch disk");
5736
- console.log(" --force overwrite even when local version >= remote");
5737
- console.log(" hivemind skillify unpull [opts] remove skills previously installed by pull");
5738
- console.log(" Options for unpull:");
5739
- console.log(" --to <project|global> where to scan (default: global)");
5740
- console.log(" --user <name> only entries authored by this user");
5741
- console.log(" --users <a,b,c> only entries authored by these users");
5742
- console.log(" --not-mine remove all pulled entries except your own");
5743
- console.log(" --dry-run show what would be removed");
5744
- console.log(" --all also remove flat-layout (locally-mined) entries");
5745
- console.log(" --legacy-cleanup also remove pre-`--author`-layout legacy `<projectKey>/` dirs");
5746
- console.log(" hivemind skillify status show per-project state");
6753
+ console.log(renderSubcommandUsageBlock());
5747
6754
  }
5748
- function takeFlagValue(args, flag) {
6755
+ function takeFlagValue2(args, flag) {
5749
6756
  const idx = args.indexOf(flag);
5750
6757
  if (idx < 0)
5751
6758
  return null;
@@ -5766,9 +6773,9 @@ function takeBooleanFlag(args, flag) {
5766
6773
  }
5767
6774
  async function pullSkills(args) {
5768
6775
  const work = [...args];
5769
- const toRaw = takeFlagValue(work, "--to") ?? "global";
5770
- const userOne = takeFlagValue(work, "--user");
5771
- const usersMany = takeFlagValue(work, "--users");
6776
+ const toRaw = takeFlagValue2(work, "--to") ?? "global";
6777
+ const userOne = takeFlagValue2(work, "--user");
6778
+ const usersMany = takeFlagValue2(work, "--users");
5772
6779
  const allUsers = takeBooleanFlag(work, "--all-users");
5773
6780
  const dryRun = takeBooleanFlag(work, "--dry-run");
5774
6781
  const force = takeBooleanFlag(work, "--force");
@@ -5807,7 +6814,7 @@ async function pullSkills(args) {
5807
6814
  console.error(`pull failed: ${e?.message ?? e}`);
5808
6815
  process.exit(1);
5809
6816
  }
5810
- const dest = toRaw === "global" ? join23(homedir13(), ".claude", "skills") : `${process.cwd()}/.claude/skills`;
6817
+ const dest = toRaw === "global" ? join27(homedir17(), ".claude", "skills") : `${process.cwd()}/.claude/skills`;
5811
6818
  const filterDesc = users.length === 0 ? "all users" : users.join(", ");
5812
6819
  console.log(`Destination: ${dest}`);
5813
6820
  console.log(`Filter: ${filterDesc}${skillName ? ` \xB7 skill='${skillName}'` : ""}${dryRun ? " \xB7 dry-run" : ""}${force ? " \xB7 force" : ""}`);
@@ -5824,9 +6831,9 @@ async function pullSkills(args) {
5824
6831
  }
5825
6832
  async function unpullSkills(args) {
5826
6833
  const work = [...args];
5827
- const toRaw = takeFlagValue(work, "--to") ?? "global";
5828
- const userOne = takeFlagValue(work, "--user");
5829
- const usersMany = takeFlagValue(work, "--users");
6834
+ const toRaw = takeFlagValue2(work, "--to") ?? "global";
6835
+ const userOne = takeFlagValue2(work, "--user");
6836
+ const usersMany = takeFlagValue2(work, "--users");
5830
6837
  const notMine = takeBooleanFlag(work, "--not-mine");
5831
6838
  const dryRun = takeBooleanFlag(work, "--dry-run");
5832
6839
  const all = takeBooleanFlag(work, "--all");
@@ -5857,7 +6864,7 @@ async function unpullSkills(args) {
5857
6864
  all,
5858
6865
  legacyCleanup
5859
6866
  });
5860
- const dest = toRaw === "global" ? join23(homedir13(), ".claude", "skills") : `${process.cwd()}/.claude/skills`;
6867
+ const dest = toRaw === "global" ? join27(homedir17(), ".claude", "skills") : `${process.cwd()}/.claude/skills`;
5861
6868
  const filterParts = [];
5862
6869
  if (users.length > 0)
5863
6870
  filterParts.push(`users=${users.join(",")}`);
@@ -5932,6 +6939,13 @@ function runSkillifyCommand(args) {
5932
6939
  console.error("Usage: hivemind skillify team <add|remove|list> [name]");
5933
6940
  process.exit(1);
5934
6941
  }
6942
+ if (sub === "mine-local") {
6943
+ runMineLocal(args.slice(1)).catch((e) => {
6944
+ console.error(`mine-local error: ${e?.message ?? e}`);
6945
+ process.exit(1);
6946
+ });
6947
+ return;
6948
+ }
5935
6949
  if (sub === "--help" || sub === "-h" || sub === "help") {
5936
6950
  usage();
5937
6951
  return;
@@ -5946,13 +6960,13 @@ if (process.argv[1] && process.argv[1].endsWith("skillify.js")) {
5946
6960
 
5947
6961
  // dist/src/cli/update.js
5948
6962
  import { execFileSync as execFileSync4 } from "node:child_process";
5949
- import { existsSync as existsSync21, readFileSync as readFileSync16, realpathSync } from "node:fs";
5950
- import { dirname as dirname6, sep } from "node:path";
6963
+ import { existsSync as existsSync25, readFileSync as readFileSync19, realpathSync } from "node:fs";
6964
+ import { dirname as dirname8, sep } from "node:path";
5951
6965
  import { fileURLToPath as fileURLToPath2 } from "node:url";
5952
6966
 
5953
6967
  // dist/src/utils/version-check.js
5954
- import { readFileSync as readFileSync15 } from "node:fs";
5955
- import { dirname as dirname5, join as join24 } from "node:path";
6968
+ import { readFileSync as readFileSync18 } from "node:fs";
6969
+ import { dirname as dirname7, join as join28 } from "node:path";
5956
6970
  function isNewer(latest, current) {
5957
6971
  const parse = (v) => v.split(".").map(Number);
5958
6972
  const [la, lb, lc] = parse(latest);
@@ -5971,24 +6985,24 @@ function detectInstallKind(argv1) {
5971
6985
  return argv1 ?? process.argv[1] ?? fileURLToPath2(import.meta.url);
5972
6986
  }
5973
6987
  })();
5974
- let dir = dirname6(realArgv1);
6988
+ let dir = dirname8(realArgv1);
5975
6989
  let installDir = null;
5976
6990
  for (let i = 0; i < 10; i++) {
5977
6991
  const pkgPath = `${dir}${sep}package.json`;
5978
6992
  try {
5979
- const pkg = JSON.parse(readFileSync16(pkgPath, "utf-8"));
6993
+ const pkg = JSON.parse(readFileSync19(pkgPath, "utf-8"));
5980
6994
  if (pkg.name === PKG_NAME || pkg.name === "hivemind") {
5981
6995
  installDir = dir;
5982
6996
  break;
5983
6997
  }
5984
6998
  } catch {
5985
6999
  }
5986
- const parent = dirname6(dir);
7000
+ const parent = dirname8(dir);
5987
7001
  if (parent === dir)
5988
7002
  break;
5989
7003
  dir = parent;
5990
7004
  }
5991
- installDir ??= dirname6(realArgv1);
7005
+ installDir ??= dirname8(realArgv1);
5992
7006
  if (realArgv1.includes(`${sep}_npx${sep}`) || realArgv1.includes(`${sep}.npx${sep}`)) {
5993
7007
  return { kind: "npx", installDir };
5994
7008
  }
@@ -5997,10 +7011,10 @@ function detectInstallKind(argv1) {
5997
7011
  }
5998
7012
  let gitDir = installDir;
5999
7013
  for (let i = 0; i < 6; i++) {
6000
- if (existsSync21(`${gitDir}${sep}.git`)) {
7014
+ if (existsSync25(`${gitDir}${sep}.git`)) {
6001
7015
  return { kind: "local-dev", installDir };
6002
7016
  }
6003
- const parent = dirname6(gitDir);
7017
+ const parent = dirname8(gitDir);
6004
7018
  if (parent === gitDir)
6005
7019
  break;
6006
7020
  gitDir = parent;
@@ -6035,7 +7049,7 @@ async function runUpdate(opts = {}) {
6035
7049
  }
6036
7050
  log(`Update available: ${current} \u2192 ${latest}`);
6037
7051
  const detected = opts.installKindOverride ?? detectInstallKind();
6038
- const spawn = opts.spawn ?? defaultSpawn;
7052
+ const spawn2 = opts.spawn ?? defaultSpawn;
6039
7053
  switch (detected.kind) {
6040
7054
  case "npm-global": {
6041
7055
  if (opts.dryRun) {
@@ -6045,7 +7059,7 @@ async function runUpdate(opts = {}) {
6045
7059
  }
6046
7060
  log(`Upgrading via npm\u2026`);
6047
7061
  try {
6048
- spawn("npm", ["install", "-g", `${PKG_NAME}@latest`]);
7062
+ spawn2("npm", ["install", "-g", `${PKG_NAME}@latest`]);
6049
7063
  } catch (e) {
6050
7064
  warn(`npm install failed: ${e.message}`);
6051
7065
  warn(`Try running it manually: npm install -g ${PKG_NAME}@latest`);
@@ -6054,7 +7068,7 @@ async function runUpdate(opts = {}) {
6054
7068
  log(``);
6055
7069
  log(`Refreshing agent bundles\u2026`);
6056
7070
  try {
6057
- spawn("hivemind", ["install", "--skip-auth"]);
7071
+ spawn2("hivemind", ["install", "--skip-auth"]);
6058
7072
  } catch (e) {
6059
7073
  warn(`Agent refresh failed: ${e.message}`);
6060
7074
  warn(`Run manually: hivemind install`);
@@ -6154,28 +7168,7 @@ Semantic search (embeddings):
6154
7168
  to run "embeddings install" automatically after installing the agent(s).
6155
7169
 
6156
7170
  Skill management (mine + share reusable Claude skills across the org):
6157
- hivemind skillify Show scope, team, install, and per-project state.
6158
- hivemind skillify pull [skill-name] Sync skills from the org table to local FS.
6159
- Options: --user <email>, --users a,b,c,
6160
- --all-users, --to <project|global>,
6161
- --dry-run, --force.
6162
- Note: every agent's SessionStart hook
6163
- auto-runs 'pull --all-users --to global'
6164
- on every session. File writes are
6165
- idempotent (skipped when local is
6166
- at-or-newer than remote). Disable via
6167
- HIVEMIND_AUTOPULL_DISABLED=1.
6168
- hivemind skillify unpull Remove skills previously installed by pull.
6169
- Options: --user, --users, --not-mine,
6170
- --to <project|global>, --dry-run,
6171
- --all (also locally-mined),
6172
- --legacy-cleanup (pre-suffix-author dirs).
6173
- hivemind skillify scope <me|team> Set the sharing scope for newly mined skills.
6174
- hivemind skillify install <project|global> Set where new skills are written.
6175
- hivemind skillify promote <name> Move a project skill to the global location.
6176
- hivemind skillify team add <username> Add a username to the team list.
6177
- hivemind skillify team remove <username> Remove a username from the team list.
6178
- hivemind skillify team list List current team members.
7171
+ ${renderCliHelpBlock()}
6179
7172
 
6180
7173
  Account / org / workspace:
6181
7174
  hivemind whoami Show current user, org, workspace.