@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.
- package/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/bundle/cli.js +1086 -93
- package/codex/bundle/capture.js +10 -5
- package/codex/bundle/commands/auth-login.js +10 -5
- package/codex/bundle/embeddings/embed-daemon.js +4 -2
- package/codex/bundle/pre-tool-use.js +10 -5
- package/codex/bundle/session-start-setup.js +10 -5
- package/codex/bundle/session-start.js +233 -136
- package/codex/bundle/shell/deeplake-shell.js +10 -5
- package/codex/bundle/skillify-worker.js +60 -21
- package/codex/bundle/stop.js +66 -25
- package/codex/bundle/wiki-worker.js +4 -2
- package/codex/skills/deeplake-memory/SKILL.md +33 -0
- package/cursor/bundle/capture.js +63 -22
- package/cursor/bundle/commands/auth-login.js +10 -5
- package/cursor/bundle/embeddings/embed-daemon.js +4 -2
- package/cursor/bundle/pre-tool-use.js +10 -5
- package/cursor/bundle/session-end.js +57 -19
- package/cursor/bundle/session-start.js +238 -87
- package/cursor/bundle/shell/deeplake-shell.js +10 -5
- package/cursor/bundle/skillify-worker.js +60 -21
- package/cursor/bundle/wiki-worker.js +4 -2
- package/hermes/bundle/capture.js +63 -22
- package/hermes/bundle/commands/auth-login.js +10 -5
- package/hermes/bundle/embeddings/embed-daemon.js +4 -2
- package/hermes/bundle/pre-tool-use.js +10 -5
- package/hermes/bundle/session-end.js +57 -19
- package/hermes/bundle/session-start.js +239 -87
- package/hermes/bundle/shell/deeplake-shell.js +10 -5
- package/hermes/bundle/skillify-worker.js +60 -21
- package/hermes/bundle/wiki-worker.js +4 -2
- package/mcp/bundle/server.js +10 -5
- package/openclaw/dist/chunks/{auth-creds-AEKS6D3P.js → auth-creds-KKTYIP27.js} +2 -1
- package/openclaw/dist/chunks/{chunk-SRCBBT4H.js → chunk-OSD5GJJ5.js} +2 -0
- package/openclaw/dist/chunks/{config-ZLH6JFJS.js → config-XEK4MJJS.js} +2 -0
- package/openclaw/dist/chunks/{index-marker-store-PGT5CW6T.js → index-marker-store-CPGF2BI7.js} +4 -2
- package/openclaw/dist/chunks/{setup-config-C35UK4LP.js → setup-config-VI54GEUM.js} +2 -0
- package/openclaw/dist/index.js +68 -19
- package/openclaw/dist/skillify-worker.js +67 -27
- package/openclaw/openclaw.plugin.json +1 -1
- package/openclaw/package.json +1 -1
- package/package.json +1 -1
- 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 (!
|
|
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
|
-
|
|
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(
|
|
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 ${
|
|
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
|
|
4805
|
-
import { homedir as
|
|
4806
|
-
import { dirname as
|
|
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 `${
|
|
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
|
|
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
|
|
5385
|
-
const skillFile = join21(
|
|
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(
|
|
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(
|
|
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
|
|
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 (!
|
|
6653
|
+
if (!existsSync24(dir)) {
|
|
5623
6654
|
console.log(`state: (no projects tracked yet)`);
|
|
5624
6655
|
return;
|
|
5625
6656
|
}
|
|
5626
|
-
const files =
|
|
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(
|
|
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" ?
|
|
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 =
|
|
5670
|
-
const globalPath =
|
|
5671
|
-
if (!
|
|
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 (
|
|
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
|
-
|
|
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(
|
|
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
|
|
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 =
|
|
5770
|
-
const userOne =
|
|
5771
|
-
const usersMany =
|
|
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" ?
|
|
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 =
|
|
5828
|
-
const userOne =
|
|
5829
|
-
const usersMany =
|
|
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" ?
|
|
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
|
|
5950
|
-
import { dirname as
|
|
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
|
|
5955
|
-
import { dirname as
|
|
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 =
|
|
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(
|
|
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 =
|
|
7000
|
+
const parent = dirname8(dir);
|
|
5987
7001
|
if (parent === dir)
|
|
5988
7002
|
break;
|
|
5989
7003
|
dir = parent;
|
|
5990
7004
|
}
|
|
5991
|
-
installDir ??=
|
|
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 (
|
|
7014
|
+
if (existsSync25(`${gitDir}${sep}.git`)) {
|
|
6001
7015
|
return { kind: "local-dev", installDir };
|
|
6002
7016
|
}
|
|
6003
|
-
const parent =
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|