@deeplake/hivemind 0.7.16 → 0.7.18
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 +294 -105
- package/codex/bundle/session-start.js +1154 -25
- package/codex/bundle/{skilify-worker.js → skillify-worker.js} +65 -30
- package/codex/bundle/stop.js +105 -68
- package/cursor/bundle/capture.js +84 -47
- package/cursor/bundle/session-end.js +82 -45
- package/cursor/bundle/session-start.js +633 -25
- package/cursor/bundle/{skilify-worker.js → skillify-worker.js} +65 -30
- package/hermes/bundle/capture.js +84 -47
- package/hermes/bundle/session-end.js +82 -45
- package/hermes/bundle/session-start.js +631 -23
- package/hermes/bundle/{skilify-worker.js → skillify-worker.js} +65 -30
- package/openclaw/dist/index.js +47 -30
- package/openclaw/dist/{skilify-worker.js → skillify-worker.js} +65 -30
- package/openclaw/openclaw.plugin.json +1 -1
- package/openclaw/package.json +1 -1
- package/openclaw/skills/SKILL.md +19 -19
- package/package.json +1 -1
- package/pi/extension-source/hivemind.ts +94 -45
|
@@ -53,7 +53,7 @@ var init_index_marker_store = __esm({
|
|
|
53
53
|
|
|
54
54
|
// dist/src/hooks/hermes/session-start.js
|
|
55
55
|
import { fileURLToPath } from "node:url";
|
|
56
|
-
import { dirname as
|
|
56
|
+
import { dirname as dirname4 } from "node:path";
|
|
57
57
|
|
|
58
58
|
// dist/src/commands/auth.js
|
|
59
59
|
import { execSync } from "node:child_process";
|
|
@@ -670,9 +670,615 @@ async function autoUpdate(creds, opts) {
|
|
|
670
670
|
log3(`agent=${opts.agent} dispatched (pid=${pid ?? "?"}) (${Date.now() - t0}ms total)`);
|
|
671
671
|
}
|
|
672
672
|
|
|
673
|
+
// dist/src/skillify/pull.js
|
|
674
|
+
import { existsSync as existsSync8, readFileSync as readFileSync7, writeFileSync as writeFileSync5, mkdirSync as mkdirSync5, renameSync as renameSync3, lstatSync as lstatSync2, readlinkSync, symlinkSync, unlinkSync as unlinkSync3 } from "node:fs";
|
|
675
|
+
import { homedir as homedir8 } from "node:os";
|
|
676
|
+
import { dirname as dirname3, join as join11 } from "node:path";
|
|
677
|
+
|
|
678
|
+
// dist/src/skillify/skill-writer.js
|
|
679
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync5, readdirSync, statSync, writeFileSync as writeFileSync3 } from "node:fs";
|
|
680
|
+
import { homedir as homedir4 } from "node:os";
|
|
681
|
+
import { join as join7 } from "node:path";
|
|
682
|
+
function assertValidSkillName(name) {
|
|
683
|
+
if (typeof name !== "string" || name.length === 0) {
|
|
684
|
+
throw new Error(`invalid skill name: empty or non-string`);
|
|
685
|
+
}
|
|
686
|
+
if (name.length > 100) {
|
|
687
|
+
throw new Error(`invalid skill name: too long (${name.length} chars)`);
|
|
688
|
+
}
|
|
689
|
+
if (name.includes("/") || name.includes("\\") || name.includes("..")) {
|
|
690
|
+
throw new Error(`invalid skill name: contains path separator or '..': ${name}`);
|
|
691
|
+
}
|
|
692
|
+
if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(name)) {
|
|
693
|
+
throw new Error(`invalid skill name: must be kebab-case (lowercase a-z, 0-9, hyphen): ${name}`);
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
function parseFrontmatter(text) {
|
|
697
|
+
if (!text.startsWith("---\n") && !text.startsWith("---\r\n"))
|
|
698
|
+
return null;
|
|
699
|
+
const end = text.indexOf("\n---", 4);
|
|
700
|
+
if (end < 0)
|
|
701
|
+
return null;
|
|
702
|
+
const head = text.slice(4, end).trim();
|
|
703
|
+
const body = text.slice(end + 4).replace(/^\r?\n/, "");
|
|
704
|
+
const fm = { source_sessions: [] };
|
|
705
|
+
let mode = "kv";
|
|
706
|
+
for (const raw of head.split(/\r?\n/)) {
|
|
707
|
+
if (mode === "sources") {
|
|
708
|
+
const m2 = raw.match(/^\s+-\s+(.+)$/);
|
|
709
|
+
if (m2) {
|
|
710
|
+
fm.source_sessions.push(m2[1].trim());
|
|
711
|
+
continue;
|
|
712
|
+
}
|
|
713
|
+
mode = "kv";
|
|
714
|
+
}
|
|
715
|
+
if (raw.startsWith("source_sessions:")) {
|
|
716
|
+
mode = "sources";
|
|
717
|
+
continue;
|
|
718
|
+
}
|
|
719
|
+
const m = raw.match(/^([a-zA-Z_]+):\s*(.*)$/);
|
|
720
|
+
if (!m)
|
|
721
|
+
continue;
|
|
722
|
+
const [, k, v] = m;
|
|
723
|
+
let val = v;
|
|
724
|
+
if (v.startsWith('"') && v.endsWith('"')) {
|
|
725
|
+
try {
|
|
726
|
+
val = JSON.parse(v);
|
|
727
|
+
} catch {
|
|
728
|
+
}
|
|
729
|
+
} else if (k === "version") {
|
|
730
|
+
const n = parseInt(v, 10);
|
|
731
|
+
if (Number.isFinite(n))
|
|
732
|
+
val = n;
|
|
733
|
+
}
|
|
734
|
+
fm[k] = val;
|
|
735
|
+
}
|
|
736
|
+
return { fm, body };
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
// dist/src/skillify/manifest.js
|
|
740
|
+
import { existsSync as existsSync6, lstatSync, mkdirSync as mkdirSync4, readFileSync as readFileSync6, renameSync as renameSync2, unlinkSync as unlinkSync2, writeFileSync as writeFileSync4 } from "node:fs";
|
|
741
|
+
import { homedir as homedir6 } from "node:os";
|
|
742
|
+
import { dirname as dirname2, join as join9 } from "node:path";
|
|
743
|
+
|
|
744
|
+
// dist/src/skillify/legacy-migration.js
|
|
745
|
+
import { existsSync as existsSync5, renameSync } from "node:fs";
|
|
746
|
+
import { homedir as homedir5 } from "node:os";
|
|
747
|
+
import { join as join8 } from "node:path";
|
|
748
|
+
var dlog = (msg) => log("skillify-migrate", msg);
|
|
749
|
+
var attempted = false;
|
|
750
|
+
function migrateLegacyStateDir() {
|
|
751
|
+
if (attempted)
|
|
752
|
+
return;
|
|
753
|
+
attempted = true;
|
|
754
|
+
const root = join8(homedir5(), ".deeplake", "state");
|
|
755
|
+
const legacy = join8(root, "skilify");
|
|
756
|
+
const current = join8(root, "skillify");
|
|
757
|
+
if (!existsSync5(legacy))
|
|
758
|
+
return;
|
|
759
|
+
if (existsSync5(current))
|
|
760
|
+
return;
|
|
761
|
+
try {
|
|
762
|
+
renameSync(legacy, current);
|
|
763
|
+
dlog(`migrated ${legacy} -> ${current}`);
|
|
764
|
+
} catch (err) {
|
|
765
|
+
const code = err.code;
|
|
766
|
+
if (code === "EXDEV" || code === "EPERM") {
|
|
767
|
+
dlog(`migration failed (${code}); leaving legacy dir in place`);
|
|
768
|
+
return;
|
|
769
|
+
}
|
|
770
|
+
throw err;
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
// dist/src/skillify/manifest.js
|
|
775
|
+
function emptyManifest() {
|
|
776
|
+
return { version: 1, entries: [] };
|
|
777
|
+
}
|
|
778
|
+
function manifestPath() {
|
|
779
|
+
return join9(homedir6(), ".deeplake", "state", "skillify", "pulled.json");
|
|
780
|
+
}
|
|
781
|
+
function loadManifest(path = manifestPath()) {
|
|
782
|
+
migrateLegacyStateDir();
|
|
783
|
+
if (!existsSync6(path))
|
|
784
|
+
return emptyManifest();
|
|
785
|
+
let raw;
|
|
786
|
+
try {
|
|
787
|
+
raw = readFileSync6(path, "utf-8");
|
|
788
|
+
} catch {
|
|
789
|
+
return emptyManifest();
|
|
790
|
+
}
|
|
791
|
+
try {
|
|
792
|
+
const parsed = JSON.parse(raw);
|
|
793
|
+
if (!parsed || typeof parsed !== "object")
|
|
794
|
+
return emptyManifest();
|
|
795
|
+
if (parsed.version !== 1 || !Array.isArray(parsed.entries))
|
|
796
|
+
return emptyManifest();
|
|
797
|
+
const entries = [];
|
|
798
|
+
for (const e of parsed.entries) {
|
|
799
|
+
if (!e || typeof e !== "object")
|
|
800
|
+
continue;
|
|
801
|
+
if (typeof e.dirName !== "string" || !e.dirName)
|
|
802
|
+
continue;
|
|
803
|
+
if (e.dirName.includes("/") || e.dirName.includes("\\") || e.dirName.includes(".."))
|
|
804
|
+
continue;
|
|
805
|
+
if (typeof e.name !== "string" || !e.name)
|
|
806
|
+
continue;
|
|
807
|
+
if (typeof e.author !== "string")
|
|
808
|
+
continue;
|
|
809
|
+
if (typeof e.installRoot !== "string" || !e.installRoot)
|
|
810
|
+
continue;
|
|
811
|
+
if (e.install !== "global" && e.install !== "project")
|
|
812
|
+
continue;
|
|
813
|
+
const symlinks = Array.isArray(e.symlinks) ? e.symlinks.filter((p) => typeof p === "string" && p.length > 0 && (p.startsWith("/") || /^[A-Za-z]:[\\/]/.test(p)) && // absolute (POSIX or Windows)
|
|
814
|
+
!p.includes("..")) : [];
|
|
815
|
+
entries.push({
|
|
816
|
+
dirName: e.dirName,
|
|
817
|
+
name: e.name,
|
|
818
|
+
author: e.author,
|
|
819
|
+
projectKey: typeof e.projectKey === "string" ? e.projectKey : "",
|
|
820
|
+
remoteVersion: typeof e.remoteVersion === "number" ? e.remoteVersion : 1,
|
|
821
|
+
install: e.install,
|
|
822
|
+
installRoot: e.installRoot,
|
|
823
|
+
pulledAt: typeof e.pulledAt === "string" ? e.pulledAt : (/* @__PURE__ */ new Date()).toISOString(),
|
|
824
|
+
symlinks
|
|
825
|
+
});
|
|
826
|
+
}
|
|
827
|
+
return { version: 1, entries };
|
|
828
|
+
} catch {
|
|
829
|
+
return emptyManifest();
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
function saveManifest(m, path = manifestPath()) {
|
|
833
|
+
migrateLegacyStateDir();
|
|
834
|
+
mkdirSync4(dirname2(path), { recursive: true });
|
|
835
|
+
const tmp = `${path}.tmp`;
|
|
836
|
+
writeFileSync4(tmp, JSON.stringify(m, null, 2) + "\n", { mode: 384 });
|
|
837
|
+
renameSync2(tmp, path);
|
|
838
|
+
}
|
|
839
|
+
function recordPull(entry, path = manifestPath()) {
|
|
840
|
+
const m = loadManifest(path);
|
|
841
|
+
const idx = m.entries.findIndex((e) => e.install === entry.install && e.installRoot === entry.installRoot && e.dirName === entry.dirName);
|
|
842
|
+
if (idx >= 0)
|
|
843
|
+
m.entries[idx] = entry;
|
|
844
|
+
else
|
|
845
|
+
m.entries.push(entry);
|
|
846
|
+
saveManifest(m, path);
|
|
847
|
+
}
|
|
848
|
+
function entriesForRoot(m, install, installRoot) {
|
|
849
|
+
return m.entries.filter((e) => e.install === install && e.installRoot === installRoot);
|
|
850
|
+
}
|
|
851
|
+
function unlinkSymlinks(paths) {
|
|
852
|
+
for (const path of paths) {
|
|
853
|
+
let st;
|
|
854
|
+
try {
|
|
855
|
+
st = lstatSync(path);
|
|
856
|
+
} catch {
|
|
857
|
+
continue;
|
|
858
|
+
}
|
|
859
|
+
if (!st.isSymbolicLink())
|
|
860
|
+
continue;
|
|
861
|
+
try {
|
|
862
|
+
unlinkSync2(path);
|
|
863
|
+
} catch {
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
function pruneOrphanedEntries(path = manifestPath()) {
|
|
868
|
+
const m = loadManifest(path);
|
|
869
|
+
const live = [];
|
|
870
|
+
let pruned = 0;
|
|
871
|
+
for (const e of m.entries) {
|
|
872
|
+
if (existsSync6(join9(e.installRoot, e.dirName))) {
|
|
873
|
+
live.push(e);
|
|
874
|
+
continue;
|
|
875
|
+
}
|
|
876
|
+
unlinkSymlinks(e.symlinks);
|
|
877
|
+
pruned++;
|
|
878
|
+
}
|
|
879
|
+
if (pruned > 0)
|
|
880
|
+
saveManifest({ version: 1, entries: live }, path);
|
|
881
|
+
return pruned;
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
// dist/src/skillify/agent-roots.js
|
|
885
|
+
import { existsSync as existsSync7 } from "node:fs";
|
|
886
|
+
import { homedir as homedir7 } from "node:os";
|
|
887
|
+
import { join as join10 } from "node:path";
|
|
888
|
+
function resolveDetected(home) {
|
|
889
|
+
const out = [];
|
|
890
|
+
const codexInstalled = existsSync7(join10(home, ".codex"));
|
|
891
|
+
const piInstalled = existsSync7(join10(home, ".pi", "agent"));
|
|
892
|
+
const hermesInstalled = existsSync7(join10(home, ".hermes"));
|
|
893
|
+
if (codexInstalled || piInstalled) {
|
|
894
|
+
out.push(join10(home, ".agents", "skills"));
|
|
895
|
+
}
|
|
896
|
+
if (hermesInstalled) {
|
|
897
|
+
out.push(join10(home, ".hermes", "skills"));
|
|
898
|
+
}
|
|
899
|
+
if (piInstalled) {
|
|
900
|
+
out.push(join10(home, ".pi", "agent", "skills"));
|
|
901
|
+
}
|
|
902
|
+
return out;
|
|
903
|
+
}
|
|
904
|
+
function detectAgentSkillsRoots(canonicalRoot, home = homedir7()) {
|
|
905
|
+
return resolveDetected(home).filter((p) => p !== canonicalRoot);
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
// dist/src/skillify/pull.js
|
|
909
|
+
function assertValidAuthor(author) {
|
|
910
|
+
if (!author)
|
|
911
|
+
throw new Error("author is empty");
|
|
912
|
+
if (author.length > 64)
|
|
913
|
+
throw new Error(`author too long (${author.length}): ${author.slice(0, 32)}\u2026`);
|
|
914
|
+
if (!/^[A-Za-z0-9_.\-@]+$/.test(author)) {
|
|
915
|
+
throw new Error(`author contains invalid characters: ${author}`);
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
function esc(s) {
|
|
919
|
+
return s.replace(/\\/g, "\\\\").replace(/'/g, "''").replace(/[\x01-\x08\x0b\x0c\x0e-\x1f\x7f]/g, "");
|
|
920
|
+
}
|
|
921
|
+
function buildPullSql(args) {
|
|
922
|
+
const where = [];
|
|
923
|
+
if (args.users.length > 0) {
|
|
924
|
+
const list = args.users.map((u) => `'${esc(u)}'`).join(", ");
|
|
925
|
+
where.push(`author IN (${list})`);
|
|
926
|
+
}
|
|
927
|
+
if (args.skillName) {
|
|
928
|
+
where.push(`name = '${esc(args.skillName)}'`);
|
|
929
|
+
}
|
|
930
|
+
const whereClause = where.length > 0 ? ` WHERE ${where.join(" AND ")}` : "";
|
|
931
|
+
return `SELECT name, project, project_key, body, version, source_agent, scope, author, description, trigger_text, source_sessions, install, created_at, updated_at FROM "${args.tableName}"${whereClause} ORDER BY project_key ASC, name ASC, version DESC`;
|
|
932
|
+
}
|
|
933
|
+
function isMissingTableError(message) {
|
|
934
|
+
if (!message)
|
|
935
|
+
return false;
|
|
936
|
+
return /Table does not exist|relation .* does not exist|no such table/i.test(message);
|
|
937
|
+
}
|
|
938
|
+
function resolvePullDestination(install, cwd) {
|
|
939
|
+
if (install === "global")
|
|
940
|
+
return join11(homedir8(), ".claude", "skills");
|
|
941
|
+
if (!cwd)
|
|
942
|
+
throw new Error("install=project requires a cwd");
|
|
943
|
+
return join11(cwd, ".claude", "skills");
|
|
944
|
+
}
|
|
945
|
+
function fanOutSymlinks(canonicalDir, dirName, agentRoots) {
|
|
946
|
+
const out = [];
|
|
947
|
+
for (const root of agentRoots) {
|
|
948
|
+
const link = join11(root, dirName);
|
|
949
|
+
let existing;
|
|
950
|
+
try {
|
|
951
|
+
existing = lstatSync2(link);
|
|
952
|
+
} catch {
|
|
953
|
+
existing = null;
|
|
954
|
+
}
|
|
955
|
+
if (existing) {
|
|
956
|
+
if (!existing.isSymbolicLink()) {
|
|
957
|
+
continue;
|
|
958
|
+
}
|
|
959
|
+
let current;
|
|
960
|
+
try {
|
|
961
|
+
current = readlinkSync(link);
|
|
962
|
+
} catch {
|
|
963
|
+
current = null;
|
|
964
|
+
}
|
|
965
|
+
if (current === canonicalDir) {
|
|
966
|
+
out.push(link);
|
|
967
|
+
continue;
|
|
968
|
+
}
|
|
969
|
+
try {
|
|
970
|
+
unlinkSync3(link);
|
|
971
|
+
} catch {
|
|
972
|
+
continue;
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
try {
|
|
976
|
+
mkdirSync5(dirname3(link), { recursive: true });
|
|
977
|
+
symlinkSync(canonicalDir, link, "dir");
|
|
978
|
+
out.push(link);
|
|
979
|
+
} catch {
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
return out;
|
|
983
|
+
}
|
|
984
|
+
function backfillSymlinks(installRoot) {
|
|
985
|
+
const manifest = loadManifest();
|
|
986
|
+
const entries = entriesForRoot(manifest, "global", installRoot);
|
|
987
|
+
if (entries.length === 0)
|
|
988
|
+
return;
|
|
989
|
+
const detected = detectAgentSkillsRoots(installRoot);
|
|
990
|
+
for (const entry of entries) {
|
|
991
|
+
const canonical = join11(entry.installRoot, entry.dirName);
|
|
992
|
+
if (!existsSync8(canonical))
|
|
993
|
+
continue;
|
|
994
|
+
const fresh = fanOutSymlinks(canonical, entry.dirName, detected);
|
|
995
|
+
if (sameSorted(fresh, entry.symlinks))
|
|
996
|
+
continue;
|
|
997
|
+
try {
|
|
998
|
+
recordPull({ ...entry, symlinks: fresh });
|
|
999
|
+
} catch {
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
function sameSorted(a, b) {
|
|
1004
|
+
if (a.length !== b.length)
|
|
1005
|
+
return false;
|
|
1006
|
+
const sa = [...a].sort();
|
|
1007
|
+
const sb = [...b].sort();
|
|
1008
|
+
for (let i = 0; i < sa.length; i++)
|
|
1009
|
+
if (sa[i] !== sb[i])
|
|
1010
|
+
return false;
|
|
1011
|
+
return true;
|
|
1012
|
+
}
|
|
1013
|
+
function selectLatestPerName(rows) {
|
|
1014
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1015
|
+
const out = [];
|
|
1016
|
+
for (const r of rows) {
|
|
1017
|
+
const name = String(r.name ?? "");
|
|
1018
|
+
const projectKey = String(r.project_key ?? "");
|
|
1019
|
+
if (!name)
|
|
1020
|
+
continue;
|
|
1021
|
+
const key = `${projectKey}\0${name}`;
|
|
1022
|
+
if (seen.has(key))
|
|
1023
|
+
continue;
|
|
1024
|
+
seen.add(key);
|
|
1025
|
+
out.push(r);
|
|
1026
|
+
}
|
|
1027
|
+
return out;
|
|
1028
|
+
}
|
|
1029
|
+
function renderSkillFile(row) {
|
|
1030
|
+
const sources = parseSourceSessions(row.source_sessions);
|
|
1031
|
+
const fm = {
|
|
1032
|
+
name: String(row.name ?? ""),
|
|
1033
|
+
description: String(row.description ?? ""),
|
|
1034
|
+
trigger: typeof row.trigger_text === "string" && row.trigger_text.length > 0 ? String(row.trigger_text) : void 0,
|
|
1035
|
+
source_sessions: sources,
|
|
1036
|
+
version: Number(row.version ?? 1),
|
|
1037
|
+
created_by_agent: String(row.source_agent ?? "unknown"),
|
|
1038
|
+
created_at: String(row.created_at ?? (/* @__PURE__ */ new Date()).toISOString()),
|
|
1039
|
+
updated_at: String(row.updated_at ?? (/* @__PURE__ */ new Date()).toISOString())
|
|
1040
|
+
};
|
|
1041
|
+
const body = String(row.body ?? "").trim();
|
|
1042
|
+
return `${renderFrontmatter(fm)}
|
|
1043
|
+
|
|
1044
|
+
${body}
|
|
1045
|
+
`;
|
|
1046
|
+
}
|
|
1047
|
+
function parseSourceSessions(v) {
|
|
1048
|
+
if (Array.isArray(v))
|
|
1049
|
+
return v.map(String);
|
|
1050
|
+
if (typeof v === "string") {
|
|
1051
|
+
try {
|
|
1052
|
+
const parsed = JSON.parse(v);
|
|
1053
|
+
if (Array.isArray(parsed))
|
|
1054
|
+
return parsed.map(String);
|
|
1055
|
+
} catch {
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
return [];
|
|
1059
|
+
}
|
|
1060
|
+
function renderFrontmatter(fm) {
|
|
1061
|
+
const lines = ["---"];
|
|
1062
|
+
lines.push(`name: ${fm.name}`);
|
|
1063
|
+
lines.push(`description: ${JSON.stringify(fm.description)}`);
|
|
1064
|
+
if (fm.trigger)
|
|
1065
|
+
lines.push(`trigger: ${JSON.stringify(fm.trigger)}`);
|
|
1066
|
+
lines.push(`source_sessions:`);
|
|
1067
|
+
for (const s of fm.source_sessions)
|
|
1068
|
+
lines.push(` - ${s}`);
|
|
1069
|
+
lines.push(`version: ${fm.version}`);
|
|
1070
|
+
lines.push(`created_by_agent: ${fm.created_by_agent}`);
|
|
1071
|
+
lines.push(`created_at: ${fm.created_at}`);
|
|
1072
|
+
lines.push(`updated_at: ${fm.updated_at}`);
|
|
1073
|
+
lines.push("---");
|
|
1074
|
+
return lines.join("\n");
|
|
1075
|
+
}
|
|
1076
|
+
function readLocalVersion(path) {
|
|
1077
|
+
if (!existsSync8(path))
|
|
1078
|
+
return null;
|
|
1079
|
+
try {
|
|
1080
|
+
const text = readFileSync7(path, "utf-8");
|
|
1081
|
+
const parsed = parseFrontmatter(text);
|
|
1082
|
+
if (!parsed)
|
|
1083
|
+
return null;
|
|
1084
|
+
const v = parsed.fm.version;
|
|
1085
|
+
return typeof v === "number" ? v : null;
|
|
1086
|
+
} catch {
|
|
1087
|
+
return null;
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
function decideAction(args) {
|
|
1091
|
+
const shouldWrite = args.localVersion === null || args.remoteVersion > args.localVersion || args.force;
|
|
1092
|
+
if (!shouldWrite)
|
|
1093
|
+
return "skipped";
|
|
1094
|
+
return args.dryRun ? "dryrun" : "wrote";
|
|
1095
|
+
}
|
|
1096
|
+
async function runPull(opts) {
|
|
1097
|
+
if (!opts.dryRun)
|
|
1098
|
+
pruneOrphanedEntries();
|
|
1099
|
+
const sql = buildPullSql({
|
|
1100
|
+
tableName: opts.tableName,
|
|
1101
|
+
users: opts.users,
|
|
1102
|
+
skillName: opts.skillName
|
|
1103
|
+
});
|
|
1104
|
+
let rows = [];
|
|
1105
|
+
try {
|
|
1106
|
+
rows = await opts.query(sql);
|
|
1107
|
+
} catch (e) {
|
|
1108
|
+
if (isMissingTableError(e?.message))
|
|
1109
|
+
rows = [];
|
|
1110
|
+
else
|
|
1111
|
+
throw e;
|
|
1112
|
+
}
|
|
1113
|
+
const latest = selectLatestPerName(rows);
|
|
1114
|
+
const root = resolvePullDestination(opts.install, opts.cwd);
|
|
1115
|
+
const summary = { scanned: latest.length, wrote: 0, skipped: 0, dryrun: 0, entries: [] };
|
|
1116
|
+
for (const row of latest) {
|
|
1117
|
+
const name = String(row.name ?? "");
|
|
1118
|
+
if (!name)
|
|
1119
|
+
continue;
|
|
1120
|
+
try {
|
|
1121
|
+
assertValidSkillName(name);
|
|
1122
|
+
} catch (e) {
|
|
1123
|
+
summary.entries.push({
|
|
1124
|
+
name,
|
|
1125
|
+
remoteVersion: Number(row.version ?? 1),
|
|
1126
|
+
localVersion: null,
|
|
1127
|
+
action: "skipped",
|
|
1128
|
+
destination: "(invalid name \u2014 skipped)",
|
|
1129
|
+
author: String(row.author ?? ""),
|
|
1130
|
+
sourceAgent: String(row.source_agent ?? "")
|
|
1131
|
+
});
|
|
1132
|
+
summary.skipped++;
|
|
1133
|
+
continue;
|
|
1134
|
+
}
|
|
1135
|
+
const author = String(row.author ?? "");
|
|
1136
|
+
if (!author) {
|
|
1137
|
+
summary.entries.push({
|
|
1138
|
+
name,
|
|
1139
|
+
remoteVersion: Number(row.version ?? 1),
|
|
1140
|
+
localVersion: null,
|
|
1141
|
+
action: "skipped",
|
|
1142
|
+
destination: "(empty author \u2014 skipped)",
|
|
1143
|
+
author: "",
|
|
1144
|
+
sourceAgent: String(row.source_agent ?? "")
|
|
1145
|
+
});
|
|
1146
|
+
summary.skipped++;
|
|
1147
|
+
continue;
|
|
1148
|
+
}
|
|
1149
|
+
let dirName;
|
|
1150
|
+
try {
|
|
1151
|
+
assertValidAuthor(author);
|
|
1152
|
+
dirName = `${name}--${author}`;
|
|
1153
|
+
} catch (e) {
|
|
1154
|
+
summary.entries.push({
|
|
1155
|
+
name,
|
|
1156
|
+
remoteVersion: Number(row.version ?? 1),
|
|
1157
|
+
localVersion: null,
|
|
1158
|
+
action: "skipped",
|
|
1159
|
+
destination: `(invalid author '${author}' \u2014 skipped)`,
|
|
1160
|
+
author,
|
|
1161
|
+
sourceAgent: String(row.source_agent ?? "")
|
|
1162
|
+
});
|
|
1163
|
+
summary.skipped++;
|
|
1164
|
+
continue;
|
|
1165
|
+
}
|
|
1166
|
+
const skillDir = join11(root, dirName);
|
|
1167
|
+
const skillFile = join11(skillDir, "SKILL.md");
|
|
1168
|
+
const remoteVersion = Number(row.version ?? 1);
|
|
1169
|
+
const localVersion = readLocalVersion(skillFile);
|
|
1170
|
+
const action = decideAction({
|
|
1171
|
+
remoteVersion,
|
|
1172
|
+
localVersion,
|
|
1173
|
+
force: opts.force ?? false,
|
|
1174
|
+
dryRun: opts.dryRun ?? false
|
|
1175
|
+
});
|
|
1176
|
+
let manifestError;
|
|
1177
|
+
if (action === "wrote") {
|
|
1178
|
+
mkdirSync5(skillDir, { recursive: true });
|
|
1179
|
+
if (existsSync8(skillFile)) {
|
|
1180
|
+
try {
|
|
1181
|
+
renameSync3(skillFile, `${skillFile}.bak`);
|
|
1182
|
+
} catch {
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
writeFileSync5(skillFile, renderSkillFile(row));
|
|
1186
|
+
const symlinks = opts.install === "global" ? fanOutSymlinks(skillDir, dirName, detectAgentSkillsRoots(root)) : [];
|
|
1187
|
+
try {
|
|
1188
|
+
recordPull({
|
|
1189
|
+
dirName,
|
|
1190
|
+
name,
|
|
1191
|
+
author,
|
|
1192
|
+
projectKey: String(row.project_key ?? ""),
|
|
1193
|
+
remoteVersion,
|
|
1194
|
+
install: opts.install,
|
|
1195
|
+
installRoot: root,
|
|
1196
|
+
pulledAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1197
|
+
symlinks
|
|
1198
|
+
});
|
|
1199
|
+
} catch (e) {
|
|
1200
|
+
manifestError = e?.message ?? String(e);
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
summary.entries.push({
|
|
1204
|
+
name,
|
|
1205
|
+
remoteVersion,
|
|
1206
|
+
localVersion,
|
|
1207
|
+
action,
|
|
1208
|
+
destination: skillFile,
|
|
1209
|
+
author: String(row.author ?? ""),
|
|
1210
|
+
sourceAgent: String(row.source_agent ?? ""),
|
|
1211
|
+
manifestError
|
|
1212
|
+
});
|
|
1213
|
+
if (action === "wrote")
|
|
1214
|
+
summary.wrote++;
|
|
1215
|
+
else if (action === "dryrun")
|
|
1216
|
+
summary.dryrun++;
|
|
1217
|
+
else
|
|
1218
|
+
summary.skipped++;
|
|
1219
|
+
}
|
|
1220
|
+
if (!opts.dryRun && opts.install === "global") {
|
|
1221
|
+
backfillSymlinks(root);
|
|
1222
|
+
}
|
|
1223
|
+
return summary;
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
// dist/src/skillify/auto-pull.js
|
|
1227
|
+
var log4 = (msg) => log("skillify-autopull", msg);
|
|
1228
|
+
var DEFAULT_TIMEOUT_MS = 5e3;
|
|
1229
|
+
function withTimeout(p, ms) {
|
|
1230
|
+
let timer = null;
|
|
1231
|
+
const timeout = new Promise((_, reject) => {
|
|
1232
|
+
timer = setTimeout(() => reject(new Error(`autopull timeout after ${ms}ms`)), ms);
|
|
1233
|
+
if (typeof timer.unref === "function")
|
|
1234
|
+
timer.unref();
|
|
1235
|
+
});
|
|
1236
|
+
return Promise.race([p, timeout]).finally(() => {
|
|
1237
|
+
if (timer)
|
|
1238
|
+
clearTimeout(timer);
|
|
1239
|
+
});
|
|
1240
|
+
}
|
|
1241
|
+
async function autoPullSkills(deps = {}) {
|
|
1242
|
+
if (process.env.HIVEMIND_AUTOPULL_DISABLED === "1") {
|
|
1243
|
+
log4("disabled via HIVEMIND_AUTOPULL_DISABLED=1");
|
|
1244
|
+
return { pulled: 0, skipped: true, reason: "disabled" };
|
|
1245
|
+
}
|
|
1246
|
+
const loadFn = deps.loadConfigFn ?? loadConfig;
|
|
1247
|
+
const config = loadFn();
|
|
1248
|
+
if (!config) {
|
|
1249
|
+
log4("skipped: not logged in");
|
|
1250
|
+
return { pulled: 0, skipped: true, reason: "not-logged-in" };
|
|
1251
|
+
}
|
|
1252
|
+
let query;
|
|
1253
|
+
if (deps.queryFn) {
|
|
1254
|
+
query = deps.queryFn;
|
|
1255
|
+
} else {
|
|
1256
|
+
const api = new DeeplakeApi(config.token, config.apiUrl, config.orgId, config.workspaceId, config.skillsTableName);
|
|
1257
|
+
query = (sql) => api.query(sql);
|
|
1258
|
+
}
|
|
1259
|
+
const install = deps.install ?? "global";
|
|
1260
|
+
const timeoutMs = deps.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
1261
|
+
try {
|
|
1262
|
+
const summary = await withTimeout(runPull({
|
|
1263
|
+
query,
|
|
1264
|
+
tableName: config.skillsTableName,
|
|
1265
|
+
install,
|
|
1266
|
+
cwd: install === "project" ? deps.cwd ?? process.cwd() : void 0,
|
|
1267
|
+
users: [],
|
|
1268
|
+
dryRun: false,
|
|
1269
|
+
force: false
|
|
1270
|
+
}), timeoutMs);
|
|
1271
|
+
log4(`pulled scanned=${summary.scanned} wrote=${summary.wrote} skipped=${summary.skipped}`);
|
|
1272
|
+
return { pulled: summary.wrote, skipped: false };
|
|
1273
|
+
} catch (e) {
|
|
1274
|
+
log4(`pull failed (swallowed): ${e?.message ?? e}`);
|
|
1275
|
+
return { pulled: 0, skipped: true, reason: "error" };
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
|
|
673
1279
|
// dist/src/hooks/hermes/session-start.js
|
|
674
|
-
var
|
|
675
|
-
var __bundleDir =
|
|
1280
|
+
var log5 = (msg) => log("hermes-session-start", msg);
|
|
1281
|
+
var __bundleDir = dirname4(fileURLToPath(import.meta.url));
|
|
676
1282
|
var context = `DEEPLAKE MEMORY: Persistent memory at ~/.deeplake/memory/ shared across sessions, users, and agents.
|
|
677
1283
|
|
|
678
1284
|
Structure: index.md (start here) \u2192 summaries/*.md \u2192 sessions/*.jsonl (last resort). Do NOT jump straight to JSONL.
|
|
@@ -692,23 +1298,23 @@ Organization management \u2014 each argument is SEPARATE (do NOT quote subcomman
|
|
|
692
1298
|
- hivemind members \u2014 list members
|
|
693
1299
|
- hivemind remove <user-id> \u2014 remove member
|
|
694
1300
|
|
|
695
|
-
SKILLS (
|
|
696
|
-
- hivemind
|
|
697
|
-
- hivemind
|
|
698
|
-
- hivemind
|
|
699
|
-
- hivemind
|
|
700
|
-
- hivemind
|
|
701
|
-
- hivemind
|
|
702
|
-
- hivemind
|
|
703
|
-
- hivemind
|
|
704
|
-
- hivemind
|
|
705
|
-
- hivemind
|
|
706
|
-
- hivemind
|
|
707
|
-
- hivemind
|
|
708
|
-
- hivemind
|
|
709
|
-
- hivemind
|
|
710
|
-
- hivemind
|
|
711
|
-
- hivemind
|
|
1301
|
+
SKILLS (skillify) \u2014 mine + share reusable skills across the org:
|
|
1302
|
+
- hivemind skillify \u2014 show scope/team/install + per-project state
|
|
1303
|
+
- hivemind skillify pull \u2014 sync project skills from the org table
|
|
1304
|
+
- hivemind skillify pull --user <email> \u2014 only that author's skills
|
|
1305
|
+
- hivemind skillify pull --users a,b,c \u2014 multiple authors (CSV)
|
|
1306
|
+
- hivemind skillify pull --all-users \u2014 explicit "no author filter"
|
|
1307
|
+
- hivemind skillify pull --to project|global \u2014 install location
|
|
1308
|
+
- hivemind skillify pull --dry-run \u2014 preview only
|
|
1309
|
+
- hivemind skillify pull --force \u2014 overwrite local (creates .bak)
|
|
1310
|
+
- hivemind skillify pull <skill-name> \u2014 pull only that skill (combines with --user)
|
|
1311
|
+
- hivemind skillify unpull \u2014 remove every skill previously installed by pull
|
|
1312
|
+
- hivemind skillify unpull --user <email> \u2014 remove only that author's pulls
|
|
1313
|
+
- hivemind skillify unpull --not-mine \u2014 remove all pulls except your own
|
|
1314
|
+
- hivemind skillify unpull --dry-run \u2014 preview without touching disk
|
|
1315
|
+
- hivemind skillify scope <me|team|org> \u2014 sharing scope for new skills
|
|
1316
|
+
- hivemind skillify install <project|global> \u2014 default install location
|
|
1317
|
+
- hivemind skillify team add|remove|list <name> \u2014 manage team list`;
|
|
712
1318
|
async function createPlaceholder(api, table, sessionId, cwd, userName, orgName, workspaceId) {
|
|
713
1319
|
const summaryPath = `/summaries/${userName}/${sessionId}.md`;
|
|
714
1320
|
const existing = await api.query(`SELECT path FROM "${table}" WHERE path = '${sqlStr(summaryPath)}' LIMIT 1`);
|
|
@@ -745,12 +1351,14 @@ async function main() {
|
|
|
745
1351
|
await api.ensureTable();
|
|
746
1352
|
await api.ensureSessionsTable(config.sessionsTableName);
|
|
747
1353
|
await createPlaceholder(api, config.tableName, sessionId, cwd, config.userName, config.orgName, config.workspaceId);
|
|
748
|
-
|
|
1354
|
+
log5("placeholder created");
|
|
749
1355
|
}
|
|
750
1356
|
} catch (e) {
|
|
751
|
-
|
|
1357
|
+
log5(`placeholder failed: ${e.message}`);
|
|
752
1358
|
}
|
|
753
1359
|
}
|
|
1360
|
+
const pullResult = await autoPullSkills();
|
|
1361
|
+
log5(`autopull: pulled=${pullResult.pulled} skipped=${pullResult.skipped}`);
|
|
754
1362
|
let versionNotice = "";
|
|
755
1363
|
const current = getInstalledVersion(__bundleDir, ".claude-plugin");
|
|
756
1364
|
if (current)
|
|
@@ -762,6 +1370,6 @@ Not logged in to Deeplake. Run: hivemind login${versionNotice}`;
|
|
|
762
1370
|
console.log(JSON.stringify({ context: additional }));
|
|
763
1371
|
}
|
|
764
1372
|
main().catch((e) => {
|
|
765
|
-
|
|
1373
|
+
log5(`fatal: ${e.message}`);
|
|
766
1374
|
process.exit(0);
|
|
767
1375
|
});
|