@deeplake/hivemind 0.7.25 → 0.7.27

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.
package/bundle/cli.js CHANGED
@@ -181,74 +181,74 @@ function pluginAlreadyInstalled() {
181
181
  return r.stdout.includes(PLUGIN_KEY);
182
182
  }
183
183
  var PLUGIN_SCOPES = ["user", "project", "local", "managed"];
184
- function resolvePluginRoot() {
185
- return join2(homedir2(), ".claude", "plugins", "hivemind");
186
- }
187
- function marketplaceHooksJsonPath() {
188
- return join2(homedir2(), ".claude", "plugins", "marketplaces", "hivemind", "claude-code", "hooks", "hooks.json");
189
- }
190
184
  function settingsJsonPath() {
191
185
  return join2(homedir2(), ".claude", "settings.json");
192
186
  }
193
- function resolveCommand(command, pluginRoot) {
194
- return command.replace(/\$\{CLAUDE_PLUGIN_ROOT\}/g, pluginRoot);
195
- }
196
- function isHivemindMatcher(matcher) {
197
- return matcher.hooks?.some((h) => {
198
- if (typeof h.command !== "string")
199
- return false;
200
- const normalized = h.command.replace(/\\/g, "/");
201
- return normalized.includes("plugins/hivemind/bundle/");
202
- }) ?? false;
187
+ var LEGACY_PATH_FRAGMENT = ".claude/plugins/hivemind/bundle/";
188
+ function isBrokenHivemindHookEntry(h) {
189
+ if (typeof h.command !== "string")
190
+ return false;
191
+ const normalized = h.command.replace(/\\/g, "/");
192
+ if (!normalized.includes(LEGACY_PATH_FRAGMENT))
193
+ return false;
194
+ const match = normalized.match(/"([^"]+\.claude\/plugins\/hivemind\/bundle\/[^"]+)"/);
195
+ const filePath = match ? match[1] : null;
196
+ if (!filePath)
197
+ return false;
198
+ return !existsSync2(filePath);
203
199
  }
204
- function syncHivemindHooksToSettings() {
205
- const hooksPath = marketplaceHooksJsonPath();
200
+ function cleanupBrokenSettingsHooks() {
206
201
  const settingsPath = settingsJsonPath();
207
- if (!existsSync2(hooksPath))
208
- return { changed: false, events: [] };
209
- let canonical;
202
+ if (!existsSync2(settingsPath))
203
+ return { removed: 0, events: [] };
204
+ let parsed;
210
205
  try {
211
- canonical = JSON.parse(readFileSync2(hooksPath, "utf-8"));
206
+ parsed = JSON.parse(readFileSync2(settingsPath, "utf-8"));
212
207
  } catch {
213
- return { changed: false, events: [] };
214
- }
215
- if (!canonical.hooks)
216
- return { changed: false, events: [] };
217
- let settings = {};
218
- if (existsSync2(settingsPath)) {
219
- try {
220
- settings = JSON.parse(readFileSync2(settingsPath, "utf-8"));
221
- } catch {
222
- return { changed: false, events: [] };
223
- }
224
- }
225
- settings.hooks = settings.hooks ?? {};
226
- const pluginRoot = resolvePluginRoot();
227
- const changedEvents = [];
228
- let changed = false;
229
- for (const [event, matchers] of Object.entries(canonical.hooks)) {
230
- const resolvedMatchers = matchers.map((m) => ({
231
- ...m.matcher !== void 0 ? { matcher: m.matcher } : {},
232
- hooks: m.hooks.map((h) => ({
233
- ...h.type !== void 0 ? { type: h.type } : {},
234
- ...h.command !== void 0 ? { command: resolveCommand(h.command, pluginRoot) } : {},
235
- ...h.timeout !== void 0 ? { timeout: h.timeout } : {},
236
- ...h.async !== void 0 ? { async: h.async } : {}
237
- }))
238
- }));
239
- const existing = settings.hooks[event] ?? [];
240
- const preserved = existing.filter((m) => !isHivemindMatcher(m));
241
- const next = [...preserved, ...resolvedMatchers];
242
- if (JSON.stringify(next) !== JSON.stringify(existing)) {
243
- settings.hooks[event] = next;
244
- changedEvents.push(event);
245
- changed = true;
208
+ return { removed: 0, events: [] };
209
+ }
210
+ if (!parsed || typeof parsed !== "object")
211
+ return { removed: 0, events: [] };
212
+ const settings = parsed;
213
+ if (!settings.hooks || typeof settings.hooks !== "object")
214
+ return { removed: 0, events: [] };
215
+ let removed = 0;
216
+ const touchedEvents = [];
217
+ for (const [event, matchers] of Object.entries(settings.hooks)) {
218
+ if (!Array.isArray(matchers))
219
+ continue;
220
+ const cleanedMatchers = [];
221
+ let eventTouched = false;
222
+ for (const m of matchers) {
223
+ if (!m || !Array.isArray(m.hooks)) {
224
+ cleanedMatchers.push(m);
225
+ continue;
226
+ }
227
+ const keptHooks = m.hooks.filter((h) => {
228
+ const broken = isBrokenHivemindHookEntry(h);
229
+ if (broken) {
230
+ removed += 1;
231
+ eventTouched = true;
232
+ }
233
+ return !broken;
234
+ });
235
+ if (keptHooks.length > 0) {
236
+ cleanedMatchers.push({ ...m, hooks: keptHooks });
237
+ } else if (m.hooks.length > 0) {
238
+ eventTouched = true;
239
+ } else {
240
+ cleanedMatchers.push(m);
241
+ }
242
+ }
243
+ if (eventTouched) {
244
+ settings.hooks[event] = cleanedMatchers;
245
+ touchedEvents.push(event);
246
246
  }
247
247
  }
248
- if (changed) {
248
+ if (removed > 0) {
249
249
  writeFileSync2(settingsPath, JSON.stringify(settings, null, 2) + "\n", "utf-8");
250
250
  }
251
- return { changed, events: changedEvents };
251
+ return { removed, events: touchedEvents };
252
252
  }
253
253
  function installClaude() {
254
254
  requireClaudeCli();
@@ -273,12 +273,12 @@ function installClaude() {
273
273
  }
274
274
  runClaude(["plugin", "enable", PLUGIN_KEY]);
275
275
  try {
276
- const sync = syncHivemindHooksToSettings();
277
- if (sync.changed) {
278
- log(` Claude Code settings.json hooks synced (${sync.events.join(", ")})`);
276
+ const cleanup = cleanupBrokenSettingsHooks();
277
+ if (cleanup.removed > 0) {
278
+ log(` Claude Code settings.json cleaned: removed ${cleanup.removed} stale hook entr${cleanup.removed === 1 ? "y" : "ies"} (events: ${cleanup.events.join(", ")})`);
279
279
  }
280
280
  } catch (e) {
281
- log(` Claude Code settings.json sync skipped: ${e?.message ?? String(e)}`);
281
+ log(` Claude Code settings.json cleanup skipped: ${e?.message ?? String(e)}`);
282
282
  }
283
283
  }
284
284
  function uninstallClaude() {
@@ -4801,9 +4801,9 @@ if (process.argv[1] && process.argv[1].endsWith("auth-login.js")) {
4801
4801
  }
4802
4802
 
4803
4803
  // 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";
4804
+ import { readdirSync as readdirSync5, existsSync as existsSync24, readFileSync as readFileSync17, mkdirSync as mkdirSync10, renameSync as renameSync4 } from "node:fs";
4805
+ import { homedir as homedir17 } from "node:os";
4806
+ import { dirname as dirname6, join as join27 } from "node:path";
4807
4807
 
4808
4808
  // dist/src/skillify/scope-config.js
4809
4809
  import { existsSync as existsSync14, mkdirSync as mkdirSync4, readFileSync as readFileSync10, writeFileSync as writeFileSync7 } from "node:fs";
@@ -4887,6 +4887,35 @@ function assertValidSkillName(name) {
4887
4887
  throw new Error(`invalid skill name: must be kebab-case (lowercase a-z, 0-9, hyphen): ${name}`);
4888
4888
  }
4889
4889
  }
4890
+ function skillDir(skillsRoot, name) {
4891
+ return join18(skillsRoot, name);
4892
+ }
4893
+ function skillPath(skillsRoot, name) {
4894
+ return join18(skillDir(skillsRoot, name), "SKILL.md");
4895
+ }
4896
+ function renderFrontmatter(fm) {
4897
+ const lines = ["---"];
4898
+ lines.push(`name: ${fm.name}`);
4899
+ lines.push(`description: ${JSON.stringify(fm.description)}`);
4900
+ if (fm.trigger)
4901
+ lines.push(`trigger: ${JSON.stringify(fm.trigger)}`);
4902
+ if (fm.author)
4903
+ lines.push(`author: ${fm.author}`);
4904
+ lines.push(`source_sessions:`);
4905
+ for (const s of fm.source_sessions)
4906
+ lines.push(` - ${s}`);
4907
+ if (fm.contributors && fm.contributors.length > 0) {
4908
+ lines.push(`contributors:`);
4909
+ for (const c of fm.contributors)
4910
+ lines.push(` - ${c}`);
4911
+ }
4912
+ lines.push(`version: ${fm.version}`);
4913
+ lines.push(`created_by_agent: ${fm.created_by_agent}`);
4914
+ lines.push(`created_at: ${fm.created_at}`);
4915
+ lines.push(`updated_at: ${fm.updated_at}`);
4916
+ lines.push("---");
4917
+ return lines.join("\n");
4918
+ }
4890
4919
  function parseFrontmatter(text) {
4891
4920
  if (!text.startsWith("---\n") && !text.startsWith("---\r\n"))
4892
4921
  return null;
@@ -4936,6 +4965,62 @@ function parseFrontmatter(text) {
4936
4965
  }
4937
4966
  return { fm, body };
4938
4967
  }
4968
+ function writeNewSkill(args) {
4969
+ assertValidSkillName(args.name);
4970
+ const dir = skillDir(args.skillsRoot, args.name);
4971
+ const path = skillPath(args.skillsRoot, args.name);
4972
+ if (existsSync15(path)) {
4973
+ throw new Error(`skill already exists at ${path}; use mergeSkill`);
4974
+ }
4975
+ mkdirSync5(dir, { recursive: true });
4976
+ const now = (/* @__PURE__ */ new Date()).toISOString();
4977
+ const author = args.author && args.author.length > 0 ? args.author : void 0;
4978
+ const contributors = author ? [author] : [];
4979
+ const fm = {
4980
+ name: args.name,
4981
+ description: args.description,
4982
+ trigger: args.trigger,
4983
+ author,
4984
+ source_sessions: args.sourceSessions,
4985
+ contributors,
4986
+ version: 1,
4987
+ created_by_agent: args.agent,
4988
+ created_at: now,
4989
+ updated_at: now
4990
+ };
4991
+ const text = `${renderFrontmatter(fm)}
4992
+
4993
+ ${args.body.trim()}
4994
+ `;
4995
+ writeFileSync8(path, text);
4996
+ return {
4997
+ path,
4998
+ action: "created",
4999
+ version: 1,
5000
+ createdAt: now,
5001
+ updatedAt: now,
5002
+ author,
5003
+ contributors
5004
+ };
5005
+ }
5006
+ function listSkills(skillsRoot) {
5007
+ if (!existsSync15(skillsRoot))
5008
+ return [];
5009
+ const out = [];
5010
+ for (const name of readdirSync2(skillsRoot)) {
5011
+ const skillFile = join18(skillsRoot, name, "SKILL.md");
5012
+ if (existsSync15(skillFile) && statSync2(skillFile).isFile()) {
5013
+ out.push({ name, body: readFileSync11(skillFile, "utf-8") });
5014
+ }
5015
+ }
5016
+ return out;
5017
+ }
5018
+ function resolveSkillsRoot(install, cwd) {
5019
+ if (install === "global") {
5020
+ return join18(homedir8(), ".claude", "skills");
5021
+ }
5022
+ return join18(cwd, ".claude", "skills");
5023
+ }
4939
5024
 
4940
5025
  // dist/src/skillify/manifest.js
4941
5026
  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 +5313,7 @@ function renderSkillFile(row) {
5228
5313
  updated_at: String(row.updated_at ?? (/* @__PURE__ */ new Date()).toISOString())
5229
5314
  };
5230
5315
  const body = String(row.body ?? "").trim();
5231
- return `${renderFrontmatter(fm)}
5316
+ return `${renderFrontmatter2(fm)}
5232
5317
 
5233
5318
  ${body}
5234
5319
  `;
@@ -5259,7 +5344,7 @@ function parseContributors(v) {
5259
5344
  }
5260
5345
  return [];
5261
5346
  }
5262
- function renderFrontmatter(fm) {
5347
+ function renderFrontmatter2(fm) {
5263
5348
  const lines = ["---"];
5264
5349
  lines.push(`name: ${fm.name}`);
5265
5350
  lines.push(`description: ${JSON.stringify(fm.description)}`);
@@ -5381,8 +5466,8 @@ async function runPull(opts) {
5381
5466
  summary.skipped++;
5382
5467
  continue;
5383
5468
  }
5384
- const skillDir = join21(root, dirName);
5385
- const skillFile = join21(skillDir, "SKILL.md");
5469
+ const skillDir2 = join21(root, dirName);
5470
+ const skillFile = join21(skillDir2, "SKILL.md");
5386
5471
  const remoteVersion = Number(row.version ?? 1);
5387
5472
  const localVersion = readLocalVersion(skillFile);
5388
5473
  const action = decideAction({
@@ -5393,7 +5478,7 @@ async function runPull(opts) {
5393
5478
  });
5394
5479
  let manifestError;
5395
5480
  if (action === "wrote") {
5396
- mkdirSync7(skillDir, { recursive: true });
5481
+ mkdirSync7(skillDir2, { recursive: true });
5397
5482
  if (existsSync18(skillFile)) {
5398
5483
  try {
5399
5484
  renameSync3(skillFile, `${skillFile}.bak`);
@@ -5401,7 +5486,7 @@ async function runPull(opts) {
5401
5486
  }
5402
5487
  }
5403
5488
  writeFileSync10(skillFile, renderSkillFile(row));
5404
- const symlinks = opts.install === "global" ? fanOutSymlinks(skillDir, dirName, detectAgentSkillsRoots(root)) : [];
5489
+ const symlinks = opts.install === "global" ? fanOutSymlinks(skillDir2, dirName, detectAgentSkillsRoots(root)) : [];
5405
5490
  try {
5406
5491
  recordPull({
5407
5492
  dirName,
@@ -5609,9 +5694,913 @@ function decideTargetForManifestEntry(entry, opts, userFilter, haveUserFilter) {
5609
5694
  return { shouldRemove: true };
5610
5695
  }
5611
5696
 
5697
+ // dist/src/commands/mine-local.js
5698
+ import { spawn } from "node:child_process";
5699
+ import { existsSync as existsSync23, mkdirSync as mkdirSync9, readFileSync as readFileSync16, writeFileSync as writeFileSync12 } from "node:fs";
5700
+ import { homedir as homedir16 } from "node:os";
5701
+ import { basename, dirname as dirname5, join as join26 } from "node:path";
5702
+
5703
+ // dist/src/skillify/local-source.js
5704
+ import { readdirSync as readdirSync4, readFileSync as readFileSync14, existsSync as existsSync20, statSync as statSync4 } from "node:fs";
5705
+ import { homedir as homedir13 } from "node:os";
5706
+ import { join as join23 } from "node:path";
5707
+ var HOME2 = homedir13();
5708
+ function encodeCwdClaudeCode(cwd) {
5709
+ return cwd.replace(/[/_]/g, "-");
5710
+ }
5711
+ function detectInstalledAgents() {
5712
+ const installs = [];
5713
+ const claudeRoot = join23(HOME2, ".claude", "projects");
5714
+ if (existsSync20(claudeRoot)) {
5715
+ installs.push({
5716
+ agent: "claude_code",
5717
+ sessionRoot: claudeRoot,
5718
+ encodeCwd: encodeCwdClaudeCode
5719
+ });
5720
+ }
5721
+ const codexRoot = join23(HOME2, ".codex", "sessions");
5722
+ if (existsSync20(codexRoot)) {
5723
+ installs.push({
5724
+ agent: "codex",
5725
+ sessionRoot: codexRoot,
5726
+ encodeCwd: () => "__cwd_unknown__"
5727
+ });
5728
+ }
5729
+ return installs;
5730
+ }
5731
+ function detectHostAgent() {
5732
+ if (process.env.CLAUDECODE === "1" || process.env.CLAUDE_CODE_ENTRYPOINT)
5733
+ return "claude_code";
5734
+ if (process.env.CODEX_HOME || process.env.CODEX_SESSION_ID)
5735
+ return "codex";
5736
+ return null;
5737
+ }
5738
+ function listLocalSessions(installs, cwd) {
5739
+ const out = [];
5740
+ for (const install of installs) {
5741
+ const cwdEncoded = install.encodeCwd(cwd);
5742
+ let subdirs = [];
5743
+ try {
5744
+ subdirs = readdirSync4(install.sessionRoot);
5745
+ } catch {
5746
+ continue;
5747
+ }
5748
+ for (const sub of subdirs) {
5749
+ const subdirPath = join23(install.sessionRoot, sub);
5750
+ try {
5751
+ if (!statSync4(subdirPath).isDirectory())
5752
+ continue;
5753
+ } catch {
5754
+ continue;
5755
+ }
5756
+ const inCwd = sub === cwdEncoded;
5757
+ let files = [];
5758
+ try {
5759
+ files = readdirSync4(subdirPath);
5760
+ } catch {
5761
+ continue;
5762
+ }
5763
+ for (const f of files) {
5764
+ if (!f.endsWith(".jsonl"))
5765
+ continue;
5766
+ const fullPath = join23(subdirPath, f);
5767
+ let stats;
5768
+ try {
5769
+ stats = statSync4(fullPath);
5770
+ } catch {
5771
+ continue;
5772
+ }
5773
+ if (!stats.isFile())
5774
+ continue;
5775
+ const sessionId = f.replace(/\.jsonl$/, "");
5776
+ out.push({
5777
+ agent: install.agent,
5778
+ path: fullPath,
5779
+ mtime: stats.mtimeMs,
5780
+ inCwd,
5781
+ sessionId
5782
+ });
5783
+ }
5784
+ }
5785
+ }
5786
+ return out;
5787
+ }
5788
+ function pickSessions(candidates, opts) {
5789
+ const { n, epsilon } = opts;
5790
+ if (n <= 0 || candidates.length === 0)
5791
+ return [];
5792
+ const sorted = [...candidates].sort((a, b) => b.mtime - a.mtime);
5793
+ const cwdQuota = Math.ceil((1 - epsilon) * n);
5794
+ const globalQuota = Math.floor(epsilon * n);
5795
+ const picked = [];
5796
+ const taken = /* @__PURE__ */ new Set();
5797
+ for (const s of sorted) {
5798
+ if (picked.length >= cwdQuota)
5799
+ break;
5800
+ if (s.inCwd && !taken.has(s.path)) {
5801
+ picked.push(s);
5802
+ taken.add(s.path);
5803
+ }
5804
+ }
5805
+ const cap2 = picked.length + globalQuota;
5806
+ for (const s of sorted) {
5807
+ if (picked.length >= cap2)
5808
+ break;
5809
+ if (!taken.has(s.path)) {
5810
+ picked.push(s);
5811
+ taken.add(s.path);
5812
+ }
5813
+ }
5814
+ for (const s of sorted) {
5815
+ if (picked.length >= n)
5816
+ break;
5817
+ if (!taken.has(s.path)) {
5818
+ picked.push(s);
5819
+ taken.add(s.path);
5820
+ }
5821
+ }
5822
+ return picked;
5823
+ }
5824
+ function nativeJsonlToRows(filePath, sessionId, agent) {
5825
+ let raw;
5826
+ try {
5827
+ raw = readFileSync14(filePath, "utf-8");
5828
+ } catch {
5829
+ return [];
5830
+ }
5831
+ const rows = [];
5832
+ let pendingAsstText;
5833
+ let pendingAsstTs;
5834
+ const flushAssistant = () => {
5835
+ if (pendingAsstText && pendingAsstText.trim().length > 0) {
5836
+ rows.push({
5837
+ type: "assistant_message",
5838
+ content: pendingAsstText,
5839
+ creation_date: pendingAsstTs,
5840
+ session_id: sessionId,
5841
+ agent
5842
+ });
5843
+ }
5844
+ pendingAsstText = void 0;
5845
+ pendingAsstTs = void 0;
5846
+ };
5847
+ for (const line of raw.split(/\n/)) {
5848
+ if (!line)
5849
+ continue;
5850
+ let obj;
5851
+ try {
5852
+ obj = JSON.parse(line);
5853
+ } catch {
5854
+ continue;
5855
+ }
5856
+ const t = obj?.type;
5857
+ const ts = obj?.timestamp ?? obj?.created_at;
5858
+ if (t === "user") {
5859
+ const c = obj?.message?.content;
5860
+ if (typeof c === "string" && c.trim().length > 0) {
5861
+ flushAssistant();
5862
+ rows.push({
5863
+ type: "user_message",
5864
+ content: c,
5865
+ creation_date: ts,
5866
+ session_id: sessionId,
5867
+ agent
5868
+ });
5869
+ }
5870
+ } else if (t === "assistant") {
5871
+ const c = obj?.message?.content;
5872
+ if (Array.isArray(c)) {
5873
+ const text = c.filter((b) => b?.type === "text" && typeof b.text === "string").map((b) => b.text).join("\n\n");
5874
+ if (text.trim().length > 0) {
5875
+ pendingAsstText = text;
5876
+ pendingAsstTs = ts;
5877
+ }
5878
+ }
5879
+ }
5880
+ }
5881
+ flushAssistant();
5882
+ return rows;
5883
+ }
5884
+
5885
+ // dist/src/skillify/extractors/index.js
5886
+ function extractPairs(rows) {
5887
+ const pairs2 = [];
5888
+ let pendingPrompt = null;
5889
+ let pendingAnswer = [];
5890
+ function flush() {
5891
+ if (pendingPrompt && pendingAnswer.length > 0) {
5892
+ pairs2.push({
5893
+ sessionId: pendingPrompt.row.session_id ?? "",
5894
+ agent: pendingPrompt.row.agent ?? null,
5895
+ date: pendingPrompt.row.creation_date ?? null,
5896
+ prompt: pendingPrompt.content,
5897
+ answer: pendingAnswer.join("\n\n")
5898
+ });
5899
+ }
5900
+ pendingPrompt = null;
5901
+ pendingAnswer = [];
5902
+ }
5903
+ for (const r of rows) {
5904
+ if (r.type === "user_message" && typeof r.content === "string") {
5905
+ flush();
5906
+ pendingPrompt = { content: r.content, row: r };
5907
+ } else if (r.type === "assistant_message" && typeof r.content === "string" && pendingPrompt) {
5908
+ if (r.content.trim().length > 0)
5909
+ pendingAnswer.push(r.content);
5910
+ }
5911
+ }
5912
+ flush();
5913
+ return pairs2;
5914
+ }
5915
+
5916
+ // dist/src/skillify/gate-runner.js
5917
+ import { execFileSync as execFileSync4 } from "node:child_process";
5918
+ import { existsSync as existsSync21 } from "node:fs";
5919
+ import { homedir as homedir14 } from "node:os";
5920
+ import { join as join24 } from "node:path";
5921
+ function findAgentBin(agent) {
5922
+ const which = (name) => {
5923
+ try {
5924
+ const out = execFileSync4("which", [name], {
5925
+ encoding: "utf-8",
5926
+ stdio: ["ignore", "pipe", "ignore"]
5927
+ });
5928
+ return out.trim() || null;
5929
+ } catch {
5930
+ return null;
5931
+ }
5932
+ };
5933
+ switch (agent) {
5934
+ case "claude_code":
5935
+ return which("claude") ?? join24(homedir14(), ".claude", "local", "claude");
5936
+ case "codex":
5937
+ return which("codex") ?? "/usr/local/bin/codex";
5938
+ case "cursor":
5939
+ return which("cursor-agent") ?? "/usr/local/bin/cursor-agent";
5940
+ case "hermes":
5941
+ return which("hermes") ?? join24(homedir14(), ".local", "bin", "hermes");
5942
+ case "pi":
5943
+ return which("pi") ?? join24(homedir14(), ".local", "bin", "pi");
5944
+ }
5945
+ }
5946
+
5947
+ // dist/src/skillify/gate-parser.js
5948
+ function extractJsonBlock(s) {
5949
+ const trimmed = s.trim();
5950
+ if (!trimmed)
5951
+ return null;
5952
+ const fenced = trimmed.match(/```(?:json)?\s*\n([\s\S]*?)\n```/);
5953
+ if (fenced)
5954
+ return fenced[1].trim();
5955
+ const start = trimmed.indexOf("{");
5956
+ if (start < 0)
5957
+ return null;
5958
+ let depth = 0;
5959
+ for (let i = start; i < trimmed.length; i++) {
5960
+ const c = trimmed[i];
5961
+ if (c === "{")
5962
+ depth++;
5963
+ else if (c === "}") {
5964
+ depth--;
5965
+ if (depth === 0)
5966
+ return trimmed.slice(start, i + 1);
5967
+ }
5968
+ }
5969
+ return null;
5970
+ }
5971
+
5972
+ // dist/src/skillify/local-manifest.js
5973
+ import { existsSync as existsSync22, mkdirSync as mkdirSync8, readFileSync as readFileSync15, writeFileSync as writeFileSync11 } from "node:fs";
5974
+ import { homedir as homedir15 } from "node:os";
5975
+ import { dirname as dirname4, join as join25 } from "node:path";
5976
+ var LOCAL_MANIFEST_PATH = join25(homedir15(), ".claude", "hivemind", "local-mined.json");
5977
+ var LOCAL_MINE_LOCK_PATH = join25(homedir15(), ".claude", "hivemind", "local-mined.lock");
5978
+ function readLocalManifest(path = LOCAL_MANIFEST_PATH) {
5979
+ if (!existsSync22(path))
5980
+ return null;
5981
+ try {
5982
+ return JSON.parse(readFileSync15(path, "utf-8"));
5983
+ } catch {
5984
+ return null;
5985
+ }
5986
+ }
5987
+ function writeLocalManifest(m, path = LOCAL_MANIFEST_PATH) {
5988
+ mkdirSync8(dirname4(path), { recursive: true });
5989
+ writeFileSync11(path, JSON.stringify(m, null, 2));
5990
+ }
5991
+
5992
+ // dist/src/commands/mine-local.js
5993
+ import { unlinkSync as unlinkSync9 } from "node:fs";
5994
+ var EPSILON = 0.3;
5995
+ var DEFAULT_N = 8;
5996
+ var PAIR_CHAR_CAP = 4e3;
5997
+ var PER_SESSION_PAIR_CAP = 30;
5998
+ var PER_SESSION_PROMPT_CAP = 12e4;
5999
+ var GATE_CONCURRENCY = 4;
6000
+ var IN_FLIGHT_MAX_AGE_MS = 6e4;
6001
+ var GATE_TIMEOUT_MS = 24e4;
6002
+ var MANIFEST_PATH = LOCAL_MANIFEST_PATH;
6003
+ function runGateViaStdin(opts) {
6004
+ return new Promise((resolve) => {
6005
+ if (opts.agent !== "claude_code") {
6006
+ resolve({
6007
+ stdout: "",
6008
+ stderr: "",
6009
+ errored: true,
6010
+ errorMessage: `stdin gate runner only supports claude_code (got ${opts.agent}); for other agents the prompt must fit in argv`
6011
+ });
6012
+ return;
6013
+ }
6014
+ if (!existsSync23(opts.bin)) {
6015
+ resolve({
6016
+ stdout: "",
6017
+ stderr: "",
6018
+ errored: true,
6019
+ errorMessage: `agent binary not found at ${opts.bin}`
6020
+ });
6021
+ return;
6022
+ }
6023
+ const args = [
6024
+ "-p",
6025
+ "--no-session-persistence",
6026
+ "--model",
6027
+ "haiku",
6028
+ "--permission-mode",
6029
+ "bypassPermissions"
6030
+ ];
6031
+ const child = spawn(opts.bin, args, {
6032
+ stdio: ["pipe", "pipe", "pipe"],
6033
+ env: { ...process.env, HIVEMIND_WIKI_WORKER: "1", HIVEMIND_CAPTURE: "false" }
6034
+ });
6035
+ let stdout = "";
6036
+ let stderr = "";
6037
+ let settled = false;
6038
+ const finish = (r) => {
6039
+ if (settled)
6040
+ return;
6041
+ settled = true;
6042
+ resolve(r);
6043
+ };
6044
+ const timer = setTimeout(() => {
6045
+ try {
6046
+ child.kill("SIGKILL");
6047
+ } catch {
6048
+ }
6049
+ finish({
6050
+ stdout,
6051
+ stderr,
6052
+ errored: true,
6053
+ errorMessage: `gate timed out after ${opts.timeoutMs}ms`
6054
+ });
6055
+ }, opts.timeoutMs);
6056
+ child.stdout.on("data", (b) => {
6057
+ stdout += b.toString("utf-8");
6058
+ });
6059
+ child.stderr.on("data", (b) => {
6060
+ stderr += b.toString("utf-8");
6061
+ });
6062
+ child.on("error", (e) => {
6063
+ clearTimeout(timer);
6064
+ finish({ stdout, stderr, errored: true, errorMessage: e.message });
6065
+ });
6066
+ child.on("close", (code) => {
6067
+ clearTimeout(timer);
6068
+ finish({
6069
+ stdout,
6070
+ stderr,
6071
+ errored: code !== 0,
6072
+ errorMessage: code !== 0 ? `claude_code CLI exited with code ${code}` : void 0
6073
+ });
6074
+ });
6075
+ child.stdin.on("error", (e) => {
6076
+ clearTimeout(timer);
6077
+ finish({ stdout, stderr, errored: true, errorMessage: `stdin write failed: ${e.message}` });
6078
+ });
6079
+ child.stdin.end(opts.prompt);
6080
+ });
6081
+ }
6082
+ var loadManifest2 = readLocalManifest;
6083
+ var saveManifest2 = writeLocalManifest;
6084
+ function truncate(s, max) {
6085
+ if (s.length <= max)
6086
+ return s;
6087
+ return s.slice(0, max) + `
6088
+ [\u2026truncated ${s.length - max} chars]`;
6089
+ }
6090
+ function renderPairsBlock(pairs2) {
6091
+ let total = 0;
6092
+ const out = [];
6093
+ for (const [i, p] of pairs2.entries()) {
6094
+ const block = `--- exchange ${i + 1} ---
6095
+ USER:
6096
+ ${truncate(p.prompt, PAIR_CHAR_CAP)}
6097
+
6098
+ ASSISTANT:
6099
+ ${truncate(p.answer, PAIR_CHAR_CAP)}
6100
+ `;
6101
+ if (total + block.length > PER_SESSION_PROMPT_CAP) {
6102
+ out.push(`[\u2026${pairs2.length - i} more exchanges omitted to stay under budget]`);
6103
+ break;
6104
+ }
6105
+ out.push(block);
6106
+ total += block.length;
6107
+ }
6108
+ return out.join("\n");
6109
+ }
6110
+ function buildSessionPrompt(pairs2, session, verdictPath) {
6111
+ return [
6112
+ `You are a skill curator examining ONE session of recent agent activity.`,
6113
+ `Your job: identify up to 3 distinct, non-overlapping reusable skills hiding in this session.`,
6114
+ `Distinct = different problem domains. Empty list is fine if nothing qualifies.`,
6115
+ ``,
6116
+ `Session: ${session.sessionId} (agent: ${session.agent})`,
6117
+ ``,
6118
+ `RULES:`,
6119
+ `- A skill qualifies if it captures a concrete, repeatable workflow OR a non-obvious`,
6120
+ ` constraint/gotcha a future engineer would benefit from knowing. Intra-session is fine \u2014`,
6121
+ ` one deep dive yielding a generalizable takeaway counts.`,
6122
+ `- Skip patterns that are obvious from reading the codebase or already in CLAUDE.md.`,
6123
+ `- Each body uses short sections (When to use, Workflow, Anti-patterns), concrete commands`,
6124
+ ` / paths / snippets drawn from the exchanges below, no marketing, no emojis.`,
6125
+ `- Each body under ~3000 characters.`,
6126
+ `- Skill names are kebab-case slugs (lowercase letters/digits/hyphens only).`,
6127
+ ``,
6128
+ `=== EXCHANGES (user prompts + assistant final answers, tool calls stripped) ===`,
6129
+ renderPairsBlock(pairs2),
6130
+ ``,
6131
+ `=== YOUR TASK ===`,
6132
+ `Output a single JSON object. You may either:`,
6133
+ ` (a) Write the JSON to this exact path using the Write tool: ${verdictPath}`,
6134
+ ` (b) Print the JSON object to stdout as your final message, nothing else.`,
6135
+ `Pick whichever you prefer. Do not do both.`,
6136
+ ``,
6137
+ `Required shape:`,
6138
+ `{`,
6139
+ ` "reason": "<one-line justification>",`,
6140
+ ` "skills": [`,
6141
+ ` {`,
6142
+ ` "name": "<kebab-case>",`,
6143
+ ` "description": "<one-line>",`,
6144
+ ` "trigger": "<short trigger>",`,
6145
+ ` "body": "<full SKILL.md body without frontmatter>"`,
6146
+ ` },`,
6147
+ ` ... up to 3 entries, or [] if nothing qualifies`,
6148
+ ` ]`,
6149
+ `}`,
6150
+ ``,
6151
+ `If you print to stdout, do not include any prose before or after the JSON.`
6152
+ ].join("\n");
6153
+ }
6154
+ function parseMultiVerdict(raw) {
6155
+ const block = extractJsonBlock(raw);
6156
+ if (!block)
6157
+ return null;
6158
+ let parsed;
6159
+ try {
6160
+ parsed = JSON.parse(block);
6161
+ } catch {
6162
+ return null;
6163
+ }
6164
+ if (!parsed || typeof parsed !== "object")
6165
+ return null;
6166
+ const skills = parsed.skills;
6167
+ if (!Array.isArray(skills))
6168
+ return null;
6169
+ const out = [];
6170
+ for (const s of skills) {
6171
+ if (!s || typeof s !== "object")
6172
+ continue;
6173
+ const name = typeof s.name === "string" ? s.name.trim() : "";
6174
+ const description = typeof s.description === "string" ? s.description.trim() : "";
6175
+ const body = typeof s.body === "string" ? s.body.trim() : "";
6176
+ const trigger = typeof s.trigger === "string" ? s.trigger.trim() : void 0;
6177
+ if (!name || !body)
6178
+ continue;
6179
+ out.push({ name, description, body, trigger });
6180
+ }
6181
+ return { reason: typeof parsed.reason === "string" ? parsed.reason : void 0, skills: out };
6182
+ }
6183
+ function gateAgentFor(host, fallback, installs) {
6184
+ const installed = new Set(installs.map((i) => i.agent));
6185
+ if (installed.has("claude_code"))
6186
+ return "claude_code";
6187
+ return host ?? fallback;
6188
+ }
6189
+ async function parallelMap(items, concurrency, fn) {
6190
+ const results = new Array(items.length);
6191
+ let cursor = 0;
6192
+ const workers = [];
6193
+ for (let w = 0; w < Math.min(concurrency, items.length); w++) {
6194
+ workers.push((async () => {
6195
+ while (true) {
6196
+ const i = cursor++;
6197
+ if (i >= items.length)
6198
+ return;
6199
+ results[i] = await fn(items[i], i);
6200
+ }
6201
+ })());
6202
+ }
6203
+ await Promise.all(workers);
6204
+ return results;
6205
+ }
6206
+ var SUMMARY_STOPWORDS = /* @__PURE__ */ new Set([
6207
+ "the",
6208
+ "and",
6209
+ "for",
6210
+ "with",
6211
+ "from",
6212
+ "into",
6213
+ "via",
6214
+ "this",
6215
+ "that",
6216
+ "your",
6217
+ "you",
6218
+ "are",
6219
+ "was",
6220
+ "were",
6221
+ "use",
6222
+ "using",
6223
+ "uses",
6224
+ "used",
6225
+ "skill",
6226
+ "when",
6227
+ "what",
6228
+ "where",
6229
+ "which",
6230
+ "while",
6231
+ "how",
6232
+ "non",
6233
+ "any",
6234
+ "all",
6235
+ "code",
6236
+ "file",
6237
+ "files",
6238
+ "way",
6239
+ "ways",
6240
+ "via"
6241
+ ]);
6242
+ function summaryTokens(s) {
6243
+ return new Set(s.toLowerCase().split(/[^a-z0-9]+/).filter((t) => t.length > 3 && !SUMMARY_STOPWORDS.has(t)));
6244
+ }
6245
+ function jaccard(a, b) {
6246
+ if (a.size === 0 || b.size === 0)
6247
+ return 0;
6248
+ let intersection = 0;
6249
+ for (const t of a)
6250
+ if (b.has(t))
6251
+ intersection++;
6252
+ return intersection / (a.size + b.size - intersection);
6253
+ }
6254
+ var OVERLAP_THRESHOLD = 0.4;
6255
+ function findOverlap(candidateDesc, others) {
6256
+ const ct = summaryTokens(candidateDesc);
6257
+ let best = null;
6258
+ for (const e of others) {
6259
+ const score = jaccard(ct, summaryTokens(e.desc));
6260
+ if (score >= OVERLAP_THRESHOLD && (!best || score > best.score)) {
6261
+ best = { name: e.name, score };
6262
+ }
6263
+ }
6264
+ return best;
6265
+ }
6266
+ function loadExistingSummaries(skillsRoot) {
6267
+ const out = [];
6268
+ for (const s of listSkills(skillsRoot)) {
6269
+ const parsed = parseFrontmatter(s.body);
6270
+ const desc = parsed?.fm.description ?? "";
6271
+ if (desc)
6272
+ out.push({ name: s.name, desc });
6273
+ }
6274
+ return out;
6275
+ }
6276
+ function takeFlagValue(args, flag) {
6277
+ const idx = args.indexOf(flag);
6278
+ if (idx < 0)
6279
+ return null;
6280
+ const v = args[idx + 1];
6281
+ if (v === void 0 || v.startsWith("--")) {
6282
+ console.error(`${flag} requires a value`);
6283
+ process.exit(1);
6284
+ }
6285
+ args.splice(idx, 2);
6286
+ return v;
6287
+ }
6288
+ function takeBoolFlag(args, flag) {
6289
+ const idx = args.indexOf(flag);
6290
+ if (idx < 0)
6291
+ return false;
6292
+ args.splice(idx, 1);
6293
+ return true;
6294
+ }
6295
+ async function runMineLocal(args) {
6296
+ let lockReleased = false;
6297
+ const releaseLock = () => {
6298
+ if (lockReleased)
6299
+ return;
6300
+ lockReleased = true;
6301
+ try {
6302
+ unlinkSync9(LOCAL_MINE_LOCK_PATH);
6303
+ } catch {
6304
+ }
6305
+ };
6306
+ process.on("exit", releaseLock);
6307
+ try {
6308
+ return await runMineLocalImpl(args);
6309
+ } finally {
6310
+ releaseLock();
6311
+ }
6312
+ }
6313
+ async function runMineLocalImpl(args) {
6314
+ const work = [...args];
6315
+ const force = takeBoolFlag(work, "--force");
6316
+ const dryRun = takeBoolFlag(work, "--dry-run");
6317
+ const nRaw = takeFlagValue(work, "--n");
6318
+ if (loadManifest2() && !force) {
6319
+ console.error(`Local skills have already been mined on this machine.`);
6320
+ console.error(`Manifest: ${MANIFEST_PATH}`);
6321
+ console.error(`Pass --force to re-mine.`);
6322
+ process.exit(1);
6323
+ }
6324
+ const installs = detectInstalledAgents();
6325
+ if (installs.length === 0) {
6326
+ console.error(`No agent session directories detected. Run a session first.`);
6327
+ process.exit(1);
6328
+ }
6329
+ console.log(`Detected installed agents: ${installs.map((i) => i.agent).join(", ")}`);
6330
+ const host = detectHostAgent();
6331
+ const fallback = installs[0].agent;
6332
+ const gateAgent = gateAgentFor(host, fallback, installs);
6333
+ if (gateAgent !== "claude_code") {
6334
+ console.error(`mine-local v1 requires the Claude Code CLI as its LLM gate.`);
6335
+ console.error(`Detected gate agent: ${gateAgent} (no claude_code session dir found at ~/.claude/projects/).`);
6336
+ console.error(`Install Claude Code, or run a Claude Code session once, then re-run.`);
6337
+ process.exit(1);
6338
+ }
6339
+ const gateBin = findAgentBin(gateAgent);
6340
+ console.log(`Gate CLI: ${gateAgent} (${gateBin})${host ? " \u2014 host-agent detected" : ""}`);
6341
+ const cwd = process.cwd();
6342
+ const rawSessions = listLocalSessions(installs, cwd);
6343
+ const now = Date.now();
6344
+ const allSessions = rawSessions.filter((s) => now - s.mtime >= IN_FLIGHT_MAX_AGE_MS);
6345
+ const dropped = rawSessions.length - allSessions.length;
6346
+ const cwdCount = allSessions.filter((s) => s.inCwd).length;
6347
+ console.log(`Found ${allSessions.length} local session(s) (${cwdCount} in cwd${dropped > 0 ? `, ${dropped} in-flight skipped` : ""})`);
6348
+ if (allSessions.length === 0) {
6349
+ console.error(`No mineable session files (all were modified within the last ${IN_FLIGHT_MAX_AGE_MS / 1e3}s).`);
6350
+ process.exit(1);
6351
+ }
6352
+ const n = nRaw === "all" ? allSessions.length : nRaw ? Math.max(1, parseInt(nRaw, 10) || DEFAULT_N) : DEFAULT_N;
6353
+ const picked = pickSessions(allSessions, { n, epsilon: EPSILON });
6354
+ console.log(`Picking ${picked.length} session(s) (\u03B5=${EPSILON}, N=${n}): ${picked.map((s) => s.sessionId.slice(0, 8)).join(", ")}`);
6355
+ if (dryRun) {
6356
+ console.log(`Dry-run: would invoke ${gateAgent} gate on ${picked.length} session(s) in parallel (concurrency=${GATE_CONCURRENCY}).`);
6357
+ return;
6358
+ }
6359
+ const tmpDir = join26(homedir16(), ".claude", "hivemind", `mine-local-${Date.now()}`);
6360
+ mkdirSync9(tmpDir, { recursive: true });
6361
+ console.log(`Running ${picked.length} gate call(s) in parallel (concurrency=${GATE_CONCURRENCY}, timeout=${GATE_TIMEOUT_MS / 1e3}s each)...`);
6362
+ const results = await parallelMap(picked, GATE_CONCURRENCY, async (s) => {
6363
+ const shortId = s.sessionId.slice(0, 8);
6364
+ const rows = nativeJsonlToRows(s.path, s.sessionId, s.agent);
6365
+ const pairs2 = extractPairs(rows);
6366
+ if (pairs2.length === 0) {
6367
+ console.log(` [${shortId}] no usable pairs \u2014 skipped`);
6368
+ return { session: s, skills: [], reason: "no pairs", error: null };
6369
+ }
6370
+ const tail = pairs2.slice(-PER_SESSION_PAIR_CAP);
6371
+ const sessionTmp = join26(tmpDir, `s-${shortId}`);
6372
+ mkdirSync9(sessionTmp, { recursive: true });
6373
+ const verdictPath = join26(sessionTmp, "verdict.json");
6374
+ const prompt = buildSessionPrompt(tail, s, verdictPath);
6375
+ writeFileSync12(join26(sessionTmp, "prompt.txt"), prompt);
6376
+ const gate = await runGateViaStdin({ agent: gateAgent, bin: gateBin, prompt, timeoutMs: GATE_TIMEOUT_MS });
6377
+ try {
6378
+ writeFileSync12(join26(sessionTmp, "gate-stdout.txt"), gate.stdout);
6379
+ if (gate.stderr)
6380
+ writeFileSync12(join26(sessionTmp, "gate-stderr.txt"), gate.stderr);
6381
+ } catch {
6382
+ }
6383
+ if (gate.errored) {
6384
+ console.log(` [${shortId}] gate failed: ${gate.errorMessage}`);
6385
+ return { session: s, skills: [], reason: null, error: gate.errorMessage ?? "gate failed" };
6386
+ }
6387
+ const verdictText = existsSync23(verdictPath) ? readFileSync16(verdictPath, "utf-8") : gate.stdout;
6388
+ const mv = parseMultiVerdict(verdictText);
6389
+ if (!mv) {
6390
+ console.log(` [${shortId}] unparseable verdict (kept at ${sessionTmp})`);
6391
+ return { session: s, skills: [], reason: null, error: "unparseable verdict" };
6392
+ }
6393
+ console.log(` [${shortId}] ${mv.skills.length} skill candidate(s) \u2014 ${mv.reason ?? "no reason given"}`);
6394
+ return { session: s, skills: mv.skills, reason: mv.reason ?? null, error: null };
6395
+ });
6396
+ const skillsRoot = resolveSkillsRoot("global", cwd);
6397
+ const totalCandidates = results.reduce((sum, r) => sum + r.skills.length, 0);
6398
+ const existingSummaries = loadExistingSummaries(skillsRoot);
6399
+ console.log("");
6400
+ console.log(`Got ${totalCandidates} candidate(s) across ${picked.length} session(s). Checking overlap against ${existingSummaries.length} installed skill(s) + each new write.`);
6401
+ if (totalCandidates === 0) {
6402
+ const existing = loadManifest2();
6403
+ saveManifest2({
6404
+ created_at: existing?.created_at ?? (/* @__PURE__ */ new Date()).toISOString(),
6405
+ entries: existing?.entries ?? []
6406
+ });
6407
+ console.log(`No skills to write.`);
6408
+ console.log(`tmp dir kept for inspection: ${tmpDir}`);
6409
+ return;
6410
+ }
6411
+ const flat = [];
6412
+ for (const r of results) {
6413
+ for (const sk of r.skills)
6414
+ flat.push({ skill: sk, session: r.session });
6415
+ }
6416
+ flat.sort((a, b) => b.session.mtime - a.session.mtime);
6417
+ const fanOutRoots = detectAgentSkillsRoots(skillsRoot);
6418
+ if (fanOutRoots.length > 0) {
6419
+ console.log(`Fan-out targets: ${fanOutRoots.join(", ")}`);
6420
+ }
6421
+ const written = [];
6422
+ const knownSummaries = [...existingSummaries];
6423
+ for (const { skill, session } of flat) {
6424
+ const overlap = findOverlap(skill.description, knownSummaries);
6425
+ if (overlap) {
6426
+ console.log(` skipped ${skill.name} \u2190 session ${session.sessionId.slice(0, 8)} (description overlaps "${overlap.name}", Jaccard=${overlap.score.toFixed(2)})`);
6427
+ continue;
6428
+ }
6429
+ try {
6430
+ const result = writeNewSkill({
6431
+ skillsRoot,
6432
+ name: skill.name,
6433
+ description: skill.description,
6434
+ trigger: skill.trigger,
6435
+ body: skill.body,
6436
+ sourceSessions: [session.sessionId],
6437
+ agent: gateAgent
6438
+ });
6439
+ const canonicalDir = dirname5(result.path);
6440
+ const symlinks = fanOutRoots.length > 0 ? fanOutSymlinks(canonicalDir, basename(canonicalDir), fanOutRoots) : [];
6441
+ const symlinkSuffix = symlinks.length > 0 ? `, fan-out \u2192 ${symlinks.length} root(s)` : "";
6442
+ console.log(` wrote ${skill.name} \u2190 session ${session.sessionId.slice(0, 8)} (${session.agent}${symlinkSuffix})`);
6443
+ written.push({ skill, session, result, symlinks });
6444
+ knownSummaries.push({ name: skill.name, desc: skill.description });
6445
+ } catch (e) {
6446
+ if (/already exists/i.test(e.message ?? "")) {
6447
+ console.log(` skipped ${skill.name} (file already exists at ${skillsRoot})`);
6448
+ } else {
6449
+ console.log(` failed ${skill.name}: ${e.message}`);
6450
+ }
6451
+ }
6452
+ }
6453
+ if (written.length > 0) {
6454
+ const existing = loadManifest2();
6455
+ const newEntries = written.map(({ skill, session, result, symlinks }) => ({
6456
+ skill_name: skill.name,
6457
+ canonical_path: result.path,
6458
+ symlinks,
6459
+ source_session_ids: [session.sessionId],
6460
+ source_session_paths: [session.path],
6461
+ source_agent: session.agent,
6462
+ gate_agent: gateAgent,
6463
+ created_at: result.createdAt,
6464
+ uploaded: false
6465
+ }));
6466
+ saveManifest2({
6467
+ created_at: existing?.created_at ?? (/* @__PURE__ */ new Date()).toISOString(),
6468
+ entries: [...existing?.entries ?? [], ...newEntries]
6469
+ });
6470
+ }
6471
+ console.log("");
6472
+ 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)).`);
6473
+ console.log(`Installed to ${skillsRoot}/ \u2014 local-only, not shared.`);
6474
+ console.log(`Sign in with 'hivemind login' to share with your team later.`);
6475
+ }
6476
+
6477
+ // dist/src/cli/skillify-spec.js
6478
+ var SKILLIFY_SPEC = [
6479
+ {
6480
+ cmd: "hivemind skillify",
6481
+ desc: "show scope, team, install, per-project state"
6482
+ },
6483
+ {
6484
+ cmd: "hivemind skillify pull",
6485
+ desc: "sync project skills from the org table to local FS",
6486
+ options: [
6487
+ { flag: "--user <email>", desc: "only skills authored by that user" },
6488
+ { flag: "--users <a,b,c>", desc: "only skills from those authors" },
6489
+ { flag: "--all-users", desc: 'explicit "no author filter" (default)' },
6490
+ { flag: "--to <project|global>", desc: "install location (project=cwd/.claude/skills, global=~/.claude/skills)" },
6491
+ { flag: "--dry-run", desc: "preview without touching disk" },
6492
+ { flag: "--force", desc: "overwrite local files even if up-to-date (creates .bak)" },
6493
+ { flag: "<skill-name>", desc: "pull only that one skill (combines with --user)" }
6494
+ ],
6495
+ 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."
6496
+ },
6497
+ {
6498
+ cmd: "hivemind skillify unpull",
6499
+ desc: "remove every skill previously installed by pull",
6500
+ options: [
6501
+ { flag: "--user <email>", desc: "remove only that author's pulls" },
6502
+ { flag: "--not-mine", desc: "remove all pulls except your own" },
6503
+ { flag: "--dry-run", desc: "preview without touching disk" }
6504
+ ]
6505
+ },
6506
+ {
6507
+ cmd: "hivemind skillify scope",
6508
+ args: "<me|team|org>",
6509
+ desc: "sharing scope for newly mined skills"
6510
+ },
6511
+ {
6512
+ cmd: "hivemind skillify install",
6513
+ args: "<project|global>",
6514
+ desc: "default install location for new skills"
6515
+ },
6516
+ {
6517
+ cmd: "hivemind skillify promote",
6518
+ args: "<skill-name>",
6519
+ desc: "move a project skill to the global location"
6520
+ },
6521
+ {
6522
+ cmd: "hivemind skillify team add|remove|list",
6523
+ args: "<name>",
6524
+ desc: "manage team member list"
6525
+ },
6526
+ {
6527
+ cmd: "hivemind skillify mine-local",
6528
+ desc: "one-shot: mine skills from local sessions (no auth needed)",
6529
+ options: [
6530
+ { flag: "--n <num|all>", desc: "how many sessions to mine (default: 8)" },
6531
+ { flag: "--force", desc: "re-run even if the manifest sentinel exists" },
6532
+ { flag: "--dry-run", desc: "stop before calling the LLM gate" }
6533
+ ]
6534
+ }
6535
+ ];
6536
+ function renderCliHelpBlock() {
6537
+ const INDENT = " ";
6538
+ const CMD_COL_WIDTH = 42;
6539
+ const lines = [];
6540
+ for (const sub of SKILLIFY_SPEC) {
6541
+ const left = sub.args ? `${sub.cmd} ${sub.args}` : sub.cmd;
6542
+ const padded = left.length >= CMD_COL_WIDTH ? `${left} ` : left.padEnd(CMD_COL_WIDTH);
6543
+ lines.push(`${INDENT}${padded}${capitalize(sub.desc)}.`);
6544
+ if (sub.options && sub.options.length > 0) {
6545
+ const optsList = sub.options.map((o) => o.flag).join(", ");
6546
+ lines.push(`${INDENT}${" ".repeat(CMD_COL_WIDTH)}Options: ${optsList}.`);
6547
+ }
6548
+ if (sub.note) {
6549
+ const noteWrapped = wrapAt(`Note: ${sub.note}`, 72);
6550
+ for (const noteLine of noteWrapped) {
6551
+ lines.push(`${INDENT}${" ".repeat(CMD_COL_WIDTH)}${noteLine}`);
6552
+ }
6553
+ }
6554
+ }
6555
+ return lines.join("\n");
6556
+ }
6557
+ function renderSubcommandUsageBlock() {
6558
+ const INDENT = " ";
6559
+ const SUB_INDENT = " ";
6560
+ const FLAG_INDENT = " ";
6561
+ const CMD_COL_WIDTH = 44;
6562
+ const FLAG_COL_WIDTH = 26;
6563
+ const lines = [];
6564
+ for (const sub of SKILLIFY_SPEC) {
6565
+ const left = sub.args ? `${sub.cmd} ${sub.args}` : sub.cmd;
6566
+ const padded = left.length >= CMD_COL_WIDTH ? `${left} ` : left.padEnd(CMD_COL_WIDTH);
6567
+ lines.push(`${INDENT}${padded}${sub.desc}`);
6568
+ if (sub.options && sub.options.length > 0) {
6569
+ const tail = sub.cmd.split(" ").slice(-1)[0];
6570
+ lines.push(`${SUB_INDENT}Options for ${tail}:`);
6571
+ for (const opt of sub.options) {
6572
+ const flagPadded = opt.flag.length >= FLAG_COL_WIDTH ? `${opt.flag} ` : opt.flag.padEnd(FLAG_COL_WIDTH);
6573
+ lines.push(`${FLAG_INDENT}${flagPadded}${opt.desc}`);
6574
+ }
6575
+ }
6576
+ }
6577
+ return lines.join("\n");
6578
+ }
6579
+ function capitalize(s) {
6580
+ return s.length === 0 ? s : s[0].toUpperCase() + s.slice(1);
6581
+ }
6582
+ function wrapAt(s, max) {
6583
+ const words = s.split(/\s+/);
6584
+ const out = [];
6585
+ let cur = "";
6586
+ for (const w of words) {
6587
+ if (cur.length === 0) {
6588
+ cur = w;
6589
+ } else if (cur.length + 1 + w.length > max) {
6590
+ out.push(cur);
6591
+ cur = w;
6592
+ } else {
6593
+ cur += " " + w;
6594
+ }
6595
+ }
6596
+ if (cur)
6597
+ out.push(cur);
6598
+ return out;
6599
+ }
6600
+
5612
6601
  // dist/src/commands/skillify.js
5613
6602
  function stateDir() {
5614
- return join23(homedir13(), ".deeplake", "state", "skillify");
6603
+ return join27(homedir17(), ".deeplake", "state", "skillify");
5615
6604
  }
5616
6605
  function showStatus() {
5617
6606
  const cfg = loadScopeConfig();
@@ -5619,11 +6608,11 @@ function showStatus() {
5619
6608
  console.log(`team: ${cfg.team.length === 0 ? "(empty)" : cfg.team.join(", ")}`);
5620
6609
  console.log(`install: ${cfg.install} (${cfg.install === "global" ? "~/.claude/skills/" : "<project>/.claude/skills/"})`);
5621
6610
  const dir = stateDir();
5622
- if (!existsSync20(dir)) {
6611
+ if (!existsSync24(dir)) {
5623
6612
  console.log(`state: (no projects tracked yet)`);
5624
6613
  return;
5625
6614
  }
5626
- const files = readdirSync4(dir).filter((f) => f.endsWith(".json") && f !== "config.json" && f !== "pulled.json" && f !== "autopull-last-run.json");
6615
+ const files = readdirSync5(dir).filter((f) => f.endsWith(".json") && f !== "config.json" && f !== "pulled.json" && f !== "autopull-last-run.json");
5627
6616
  if (files.length === 0) {
5628
6617
  console.log(`state: (no projects tracked yet)`);
5629
6618
  return;
@@ -5631,7 +6620,7 @@ function showStatus() {
5631
6620
  console.log(`state: ${files.length} project(s) tracked`);
5632
6621
  for (const f of files) {
5633
6622
  try {
5634
- const s = JSON.parse(readFileSync14(join23(dir, f), "utf-8"));
6623
+ const s = JSON.parse(readFileSync17(join27(dir, f), "utf-8"));
5635
6624
  const last = typeof s.updatedAt === "number" ? new Date(s.updatedAt).toISOString() : s.lastDate ?? "never";
5636
6625
  const skills = Array.isArray(s.skillsGenerated) && s.skillsGenerated.length > 0 ? s.skillsGenerated.join(", ") : "none";
5637
6626
  console.log(` - ${s.project} (counter=${s.counter}, last=${last}, skills=${skills})`);
@@ -5658,7 +6647,7 @@ function setInstall(loc) {
5658
6647
  }
5659
6648
  const cfg = loadScopeConfig();
5660
6649
  saveScopeConfig({ ...cfg, install: loc });
5661
- const path = loc === "global" ? join23(homedir13(), ".claude", "skills") : "<cwd>/.claude/skills";
6650
+ const path = loc === "global" ? join27(homedir17(), ".claude", "skills") : "<cwd>/.claude/skills";
5662
6651
  console.log(`Install location set to '${loc}'. New skills will be written to ${path}/<name>/SKILL.md.`);
5663
6652
  }
5664
6653
  function promoteSkill(name, cwd) {
@@ -5666,17 +6655,17 @@ function promoteSkill(name, cwd) {
5666
6655
  console.error("Usage: hivemind skillify promote <skill-name>");
5667
6656
  process.exit(1);
5668
6657
  }
5669
- const projectPath = join23(cwd, ".claude", "skills", name);
5670
- const globalPath = join23(homedir13(), ".claude", "skills", name);
5671
- if (!existsSync20(join23(projectPath, "SKILL.md"))) {
6658
+ const projectPath = join27(cwd, ".claude", "skills", name);
6659
+ const globalPath = join27(homedir17(), ".claude", "skills", name);
6660
+ if (!existsSync24(join27(projectPath, "SKILL.md"))) {
5672
6661
  console.error(`Skill '${name}' not found at ${projectPath}/SKILL.md`);
5673
6662
  process.exit(1);
5674
6663
  }
5675
- if (existsSync20(join23(globalPath, "SKILL.md"))) {
6664
+ if (existsSync24(join27(globalPath, "SKILL.md"))) {
5676
6665
  console.error(`Skill '${name}' already exists at ${globalPath}/SKILL.md \u2014 refusing to overwrite. Remove it first or rename the project skill.`);
5677
6666
  process.exit(1);
5678
6667
  }
5679
- mkdirSync8(dirname4(globalPath), { recursive: true });
6668
+ mkdirSync10(dirname6(globalPath), { recursive: true });
5680
6669
  renameSync4(projectPath, globalPath);
5681
6670
  console.log(`Promoted '${name}' from ${projectPath} \u2192 ${globalPath}.`);
5682
6671
  }
@@ -5719,33 +6708,9 @@ function teamList() {
5719
6708
  }
5720
6709
  function usage() {
5721
6710
  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");
6711
+ console.log(renderSubcommandUsageBlock());
5747
6712
  }
5748
- function takeFlagValue(args, flag) {
6713
+ function takeFlagValue2(args, flag) {
5749
6714
  const idx = args.indexOf(flag);
5750
6715
  if (idx < 0)
5751
6716
  return null;
@@ -5766,9 +6731,9 @@ function takeBooleanFlag(args, flag) {
5766
6731
  }
5767
6732
  async function pullSkills(args) {
5768
6733
  const work = [...args];
5769
- const toRaw = takeFlagValue(work, "--to") ?? "global";
5770
- const userOne = takeFlagValue(work, "--user");
5771
- const usersMany = takeFlagValue(work, "--users");
6734
+ const toRaw = takeFlagValue2(work, "--to") ?? "global";
6735
+ const userOne = takeFlagValue2(work, "--user");
6736
+ const usersMany = takeFlagValue2(work, "--users");
5772
6737
  const allUsers = takeBooleanFlag(work, "--all-users");
5773
6738
  const dryRun = takeBooleanFlag(work, "--dry-run");
5774
6739
  const force = takeBooleanFlag(work, "--force");
@@ -5807,7 +6772,7 @@ async function pullSkills(args) {
5807
6772
  console.error(`pull failed: ${e?.message ?? e}`);
5808
6773
  process.exit(1);
5809
6774
  }
5810
- const dest = toRaw === "global" ? join23(homedir13(), ".claude", "skills") : `${process.cwd()}/.claude/skills`;
6775
+ const dest = toRaw === "global" ? join27(homedir17(), ".claude", "skills") : `${process.cwd()}/.claude/skills`;
5811
6776
  const filterDesc = users.length === 0 ? "all users" : users.join(", ");
5812
6777
  console.log(`Destination: ${dest}`);
5813
6778
  console.log(`Filter: ${filterDesc}${skillName ? ` \xB7 skill='${skillName}'` : ""}${dryRun ? " \xB7 dry-run" : ""}${force ? " \xB7 force" : ""}`);
@@ -5824,9 +6789,9 @@ async function pullSkills(args) {
5824
6789
  }
5825
6790
  async function unpullSkills(args) {
5826
6791
  const work = [...args];
5827
- const toRaw = takeFlagValue(work, "--to") ?? "global";
5828
- const userOne = takeFlagValue(work, "--user");
5829
- const usersMany = takeFlagValue(work, "--users");
6792
+ const toRaw = takeFlagValue2(work, "--to") ?? "global";
6793
+ const userOne = takeFlagValue2(work, "--user");
6794
+ const usersMany = takeFlagValue2(work, "--users");
5830
6795
  const notMine = takeBooleanFlag(work, "--not-mine");
5831
6796
  const dryRun = takeBooleanFlag(work, "--dry-run");
5832
6797
  const all = takeBooleanFlag(work, "--all");
@@ -5857,7 +6822,7 @@ async function unpullSkills(args) {
5857
6822
  all,
5858
6823
  legacyCleanup
5859
6824
  });
5860
- const dest = toRaw === "global" ? join23(homedir13(), ".claude", "skills") : `${process.cwd()}/.claude/skills`;
6825
+ const dest = toRaw === "global" ? join27(homedir17(), ".claude", "skills") : `${process.cwd()}/.claude/skills`;
5861
6826
  const filterParts = [];
5862
6827
  if (users.length > 0)
5863
6828
  filterParts.push(`users=${users.join(",")}`);
@@ -5932,6 +6897,13 @@ function runSkillifyCommand(args) {
5932
6897
  console.error("Usage: hivemind skillify team <add|remove|list> [name]");
5933
6898
  process.exit(1);
5934
6899
  }
6900
+ if (sub === "mine-local") {
6901
+ runMineLocal(args.slice(1)).catch((e) => {
6902
+ console.error(`mine-local error: ${e?.message ?? e}`);
6903
+ process.exit(1);
6904
+ });
6905
+ return;
6906
+ }
5935
6907
  if (sub === "--help" || sub === "-h" || sub === "help") {
5936
6908
  usage();
5937
6909
  return;
@@ -5945,14 +6917,14 @@ if (process.argv[1] && process.argv[1].endsWith("skillify.js")) {
5945
6917
  }
5946
6918
 
5947
6919
  // dist/src/cli/update.js
5948
- 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";
6920
+ import { execFileSync as execFileSync5 } from "node:child_process";
6921
+ import { existsSync as existsSync25, readFileSync as readFileSync19, realpathSync } from "node:fs";
6922
+ import { dirname as dirname8, sep } from "node:path";
5951
6923
  import { fileURLToPath as fileURLToPath2 } from "node:url";
5952
6924
 
5953
6925
  // 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";
6926
+ import { readFileSync as readFileSync18 } from "node:fs";
6927
+ import { dirname as dirname7, join as join28 } from "node:path";
5956
6928
  function isNewer(latest, current) {
5957
6929
  const parse = (v) => v.split(".").map(Number);
5958
6930
  const [la, lb, lc] = parse(latest);
@@ -5971,24 +6943,24 @@ function detectInstallKind(argv1) {
5971
6943
  return argv1 ?? process.argv[1] ?? fileURLToPath2(import.meta.url);
5972
6944
  }
5973
6945
  })();
5974
- let dir = dirname6(realArgv1);
6946
+ let dir = dirname8(realArgv1);
5975
6947
  let installDir = null;
5976
6948
  for (let i = 0; i < 10; i++) {
5977
6949
  const pkgPath = `${dir}${sep}package.json`;
5978
6950
  try {
5979
- const pkg = JSON.parse(readFileSync16(pkgPath, "utf-8"));
6951
+ const pkg = JSON.parse(readFileSync19(pkgPath, "utf-8"));
5980
6952
  if (pkg.name === PKG_NAME || pkg.name === "hivemind") {
5981
6953
  installDir = dir;
5982
6954
  break;
5983
6955
  }
5984
6956
  } catch {
5985
6957
  }
5986
- const parent = dirname6(dir);
6958
+ const parent = dirname8(dir);
5987
6959
  if (parent === dir)
5988
6960
  break;
5989
6961
  dir = parent;
5990
6962
  }
5991
- installDir ??= dirname6(realArgv1);
6963
+ installDir ??= dirname8(realArgv1);
5992
6964
  if (realArgv1.includes(`${sep}_npx${sep}`) || realArgv1.includes(`${sep}.npx${sep}`)) {
5993
6965
  return { kind: "npx", installDir };
5994
6966
  }
@@ -5997,10 +6969,10 @@ function detectInstallKind(argv1) {
5997
6969
  }
5998
6970
  let gitDir = installDir;
5999
6971
  for (let i = 0; i < 6; i++) {
6000
- if (existsSync21(`${gitDir}${sep}.git`)) {
6972
+ if (existsSync25(`${gitDir}${sep}.git`)) {
6001
6973
  return { kind: "local-dev", installDir };
6002
6974
  }
6003
- const parent = dirname6(gitDir);
6975
+ const parent = dirname8(gitDir);
6004
6976
  if (parent === gitDir)
6005
6977
  break;
6006
6978
  gitDir = parent;
@@ -6019,7 +6991,7 @@ async function getLatestNpmVersion(timeoutMs = 5e3) {
6019
6991
  }
6020
6992
  }
6021
6993
  var defaultSpawn = (cmd, args) => {
6022
- execFileSync4(cmd, args, { stdio: "inherit" });
6994
+ execFileSync5(cmd, args, { stdio: "inherit" });
6023
6995
  };
6024
6996
  async function runUpdate(opts = {}) {
6025
6997
  const current = opts.currentVersionOverride ?? getVersion();
@@ -6035,7 +7007,7 @@ async function runUpdate(opts = {}) {
6035
7007
  }
6036
7008
  log(`Update available: ${current} \u2192 ${latest}`);
6037
7009
  const detected = opts.installKindOverride ?? detectInstallKind();
6038
- const spawn = opts.spawn ?? defaultSpawn;
7010
+ const spawn2 = opts.spawn ?? defaultSpawn;
6039
7011
  switch (detected.kind) {
6040
7012
  case "npm-global": {
6041
7013
  if (opts.dryRun) {
@@ -6045,7 +7017,7 @@ async function runUpdate(opts = {}) {
6045
7017
  }
6046
7018
  log(`Upgrading via npm\u2026`);
6047
7019
  try {
6048
- spawn("npm", ["install", "-g", `${PKG_NAME}@latest`]);
7020
+ spawn2("npm", ["install", "-g", `${PKG_NAME}@latest`]);
6049
7021
  } catch (e) {
6050
7022
  warn(`npm install failed: ${e.message}`);
6051
7023
  warn(`Try running it manually: npm install -g ${PKG_NAME}@latest`);
@@ -6054,7 +7026,7 @@ async function runUpdate(opts = {}) {
6054
7026
  log(``);
6055
7027
  log(`Refreshing agent bundles\u2026`);
6056
7028
  try {
6057
- spawn("hivemind", ["install", "--skip-auth"]);
7029
+ spawn2("hivemind", ["install", "--skip-auth"]);
6058
7030
  } catch (e) {
6059
7031
  warn(`Agent refresh failed: ${e.message}`);
6060
7032
  warn(`Run manually: hivemind install`);
@@ -6154,28 +7126,7 @@ Semantic search (embeddings):
6154
7126
  to run "embeddings install" automatically after installing the agent(s).
6155
7127
 
6156
7128
  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.
7129
+ ${renderCliHelpBlock()}
6179
7130
 
6180
7131
  Account / org / workspace:
6181
7132
  hivemind whoami Show current user, org, workspace.