@deeplake/hivemind 0.7.25 → 0.7.27
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/bundle/cli.js +1102 -151
- package/codex/bundle/session-start.js +223 -131
- package/codex/skills/deeplake-memory/SKILL.md +33 -0
- package/cursor/bundle/session-start.js +228 -82
- package/hermes/bundle/session-start.js +229 -82
- package/openclaw/dist/index.js +1 -1
- 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
|
@@ -26,12 +26,14 @@
|
|
|
26
26
|
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
27
27
|
import {
|
|
28
28
|
readFileSync, existsSync, appendFileSync, mkdirSync, writeFileSync,
|
|
29
|
-
openSync, closeSync, renameSync,
|
|
29
|
+
openSync, closeSync, renameSync, readdirSync, statSync, unlinkSync,
|
|
30
|
+
constants as fsConstants,
|
|
30
31
|
} from "node:fs";
|
|
31
32
|
import { homedir, tmpdir } from "node:os";
|
|
32
33
|
import { join, dirname } from "node:path";
|
|
34
|
+
import { fileURLToPath } from "node:url";
|
|
33
35
|
import { connect } from "node:net";
|
|
34
|
-
import { spawn, spawnSync, execSync } from "node:child_process";
|
|
36
|
+
import { spawn, spawnSync, execSync, execFileSync } from "node:child_process";
|
|
35
37
|
import { createHash } from "node:crypto";
|
|
36
38
|
|
|
37
39
|
// ---------- diagnostic logging --------------------------------------------------
|
|
@@ -691,6 +693,145 @@ function textResult(text: string) {
|
|
|
691
693
|
|
|
692
694
|
// ---------- main extension -----------------------------------------------------
|
|
693
695
|
|
|
696
|
+
// MIRROR of src/skillify/local-manifest.ts countLocalManifestEntries.
|
|
697
|
+
//
|
|
698
|
+
// pi's extension cannot import from src/. Read the manifest inline so the
|
|
699
|
+
// SessionStart hook can surface "you have N local skills" when the user
|
|
700
|
+
// isn't signed in. Returns 0 on any error (missing file, parse failure)
|
|
701
|
+
// so the message is silently omitted in those cases.
|
|
702
|
+
const PI_LOCAL_MANIFEST_PATH = join(homedir(), ".claude", "hivemind", "local-mined.json");
|
|
703
|
+
|
|
704
|
+
function piCountLocalManifestEntries(): number {
|
|
705
|
+
try {
|
|
706
|
+
if (!existsSync(PI_LOCAL_MANIFEST_PATH)) return 0;
|
|
707
|
+
const data = JSON.parse(readFileSync(PI_LOCAL_MANIFEST_PATH, "utf-8"));
|
|
708
|
+
return Array.isArray(data?.entries) ? data.entries.length : 0;
|
|
709
|
+
} catch {
|
|
710
|
+
return 0;
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
// MIRROR of src/skillify/spawn-mine-local-worker.ts maybeAutoMineLocal().
|
|
715
|
+
// First-impression bootstrap: when an unauthenticated pi session sees
|
|
716
|
+
// past Claude Code transcripts but no local mining manifest, spawn the
|
|
717
|
+
// `hivemind` CLI in the background. THIS session sees the standard
|
|
718
|
+
// "not logged in" message; the NEXT pi session sees the mined-count
|
|
719
|
+
// CTA from piCountLocalManifestEntries above.
|
|
720
|
+
const PI_LOCAL_MINE_LOCK_PATH = join(homedir(), ".claude", "hivemind", "local-mined.lock");
|
|
721
|
+
const PI_AUTO_MINE_LOG_PATH = join(homedir(), ".claude", "hooks", "mine-local.log");
|
|
722
|
+
const PI_CLAUDE_PROJECTS_DIR = join(homedir(), ".claude", "projects");
|
|
723
|
+
const PI_LOCK_STALE_MS = 15 * 60 * 1000;
|
|
724
|
+
|
|
725
|
+
function piMaybeAutoMineLocal(): boolean {
|
|
726
|
+
try {
|
|
727
|
+
if (existsSync(PI_LOCAL_MANIFEST_PATH)) return false;
|
|
728
|
+
if (existsSync(PI_LOCAL_MINE_LOCK_PATH)) {
|
|
729
|
+
let stale = false;
|
|
730
|
+
try {
|
|
731
|
+
const stats = statSync(PI_LOCAL_MINE_LOCK_PATH);
|
|
732
|
+
stale = Date.now() - stats.mtimeMs > PI_LOCK_STALE_MS;
|
|
733
|
+
} catch { /* not stale */ }
|
|
734
|
+
if (!stale) return false;
|
|
735
|
+
try { unlinkSync(PI_LOCAL_MINE_LOCK_PATH); } catch { return false; }
|
|
736
|
+
}
|
|
737
|
+
if (!existsSync(PI_CLAUDE_PROJECTS_DIR)) return false;
|
|
738
|
+
// cheap existence-of-jsonl check (1-level walk)
|
|
739
|
+
let hasJsonl = false;
|
|
740
|
+
try {
|
|
741
|
+
for (const sub of readdirSync(PI_CLAUDE_PROJECTS_DIR)) {
|
|
742
|
+
let files: string[] = [];
|
|
743
|
+
try { files = readdirSync(join(PI_CLAUDE_PROJECTS_DIR, sub)); } catch { continue; }
|
|
744
|
+
if (files.some((f: string) => f.endsWith(".jsonl"))) { hasJsonl = true; break; }
|
|
745
|
+
}
|
|
746
|
+
} catch { return false; }
|
|
747
|
+
if (!hasJsonl) return false;
|
|
748
|
+
|
|
749
|
+
// Prefer the sibling bundled CLI (same plugin install as this hook
|
|
750
|
+
// extension → guaranteed to know `mine-local`). Fall back to PATH for
|
|
751
|
+
// unusual install layouts. Mirrors findHivemindLauncher() in
|
|
752
|
+
// src/skillify/spawn-mine-local-worker.ts.
|
|
753
|
+
let launcher: { kind: "node-script" | "bin"; path: string } | null = null;
|
|
754
|
+
try {
|
|
755
|
+
const thisDir = dirname(fileURLToPath(import.meta.url));
|
|
756
|
+
const cliPath = join(thisDir, "..", "..", "bundle", "cli.js");
|
|
757
|
+
if (existsSync(cliPath)) launcher = { kind: "node-script", path: cliPath };
|
|
758
|
+
} catch { /* fall through to which */ }
|
|
759
|
+
if (!launcher) {
|
|
760
|
+
try {
|
|
761
|
+
const out = execFileSync("which", ["hivemind"], { encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"] });
|
|
762
|
+
const bin = String(out).trim();
|
|
763
|
+
if (bin) launcher = { kind: "bin", path: bin };
|
|
764
|
+
} catch { return false; }
|
|
765
|
+
}
|
|
766
|
+
if (!launcher) return false;
|
|
767
|
+
|
|
768
|
+
// Acquire the lock (exclusive create); if another pi session got
|
|
769
|
+
// here first, skip.
|
|
770
|
+
try {
|
|
771
|
+
mkdirSync(dirname(PI_LOCAL_MINE_LOCK_PATH), { recursive: true });
|
|
772
|
+
const fd = openSync(PI_LOCAL_MINE_LOCK_PATH, "wx");
|
|
773
|
+
closeSync(fd);
|
|
774
|
+
} catch { return false; }
|
|
775
|
+
|
|
776
|
+
try {
|
|
777
|
+
mkdirSync(dirname(PI_AUTO_MINE_LOG_PATH), { recursive: true });
|
|
778
|
+
const out = openSync(PI_AUTO_MINE_LOG_PATH, "a");
|
|
779
|
+
const [cmd, args]: [string, string[]] = launcher.kind === "node-script"
|
|
780
|
+
? [process.execPath, [launcher.path, "skillify", "mine-local"]]
|
|
781
|
+
: [launcher.path, ["skillify", "mine-local"]];
|
|
782
|
+
const child = spawn(cmd, args, {
|
|
783
|
+
detached: true,
|
|
784
|
+
stdio: ["ignore", out, out],
|
|
785
|
+
env: process.env,
|
|
786
|
+
});
|
|
787
|
+
closeSync(out);
|
|
788
|
+
child.unref();
|
|
789
|
+
return true;
|
|
790
|
+
} catch {
|
|
791
|
+
try { unlinkSync(PI_LOCAL_MINE_LOCK_PATH); } catch { /* best-effort */ }
|
|
792
|
+
return false;
|
|
793
|
+
}
|
|
794
|
+
} catch { return false; }
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
// MIRROR of src/cli/skillify-spec.ts SKILLIFY_COMMANDS.
|
|
798
|
+
//
|
|
799
|
+
// pi extensions are shipped as a single self-contained .ts file loaded by
|
|
800
|
+
// pi's runtime, so they cannot import from src/. This array is hand-kept
|
|
801
|
+
// in sync with the canonical spec; the agents-deployment-session-start-injection
|
|
802
|
+
// skill documents the rule and there is a vitest drift-scan that fails the
|
|
803
|
+
// build if the two lists diverge.
|
|
804
|
+
const PI_SKILLIFY_COMMANDS: { cmd: string; desc: string }[] = [
|
|
805
|
+
{ cmd: "hivemind skillify", desc: "show scope, team, install, per-project state" },
|
|
806
|
+
{ cmd: "hivemind skillify pull", desc: "sync project skills from the org table to local FS" },
|
|
807
|
+
{ cmd: "hivemind skillify pull --user <email>", desc: "only skills authored by that user" },
|
|
808
|
+
{ cmd: "hivemind skillify pull --users <a,b,c>", desc: "only skills from those authors" },
|
|
809
|
+
{ cmd: "hivemind skillify pull --all-users", desc: 'explicit "no author filter" (default)' },
|
|
810
|
+
{ cmd: "hivemind skillify pull --to <project|global>", desc: "install location (project=cwd/.claude/skills, global=~/.claude/skills)" },
|
|
811
|
+
{ cmd: "hivemind skillify pull --dry-run", desc: "preview without touching disk" },
|
|
812
|
+
{ cmd: "hivemind skillify pull --force", desc: "overwrite local files even if up-to-date (creates .bak)" },
|
|
813
|
+
{ cmd: "hivemind skillify pull <skill-name>", desc: "pull only that one skill (combines with --user)" },
|
|
814
|
+
{ cmd: "hivemind skillify unpull", desc: "remove every skill previously installed by pull" },
|
|
815
|
+
{ cmd: "hivemind skillify unpull --user <email>", desc: "remove only that author's pulls" },
|
|
816
|
+
{ cmd: "hivemind skillify unpull --not-mine", desc: "remove all pulls except your own" },
|
|
817
|
+
{ cmd: "hivemind skillify unpull --dry-run", desc: "preview without touching disk" },
|
|
818
|
+
{ cmd: "hivemind skillify scope <me|team|org>", desc: "sharing scope for newly mined skills" },
|
|
819
|
+
{ cmd: "hivemind skillify install <project|global>", desc: "default install location for new skills" },
|
|
820
|
+
{ cmd: "hivemind skillify promote <skill-name>", desc: "move a project skill to the global location" },
|
|
821
|
+
{ cmd: "hivemind skillify team add|remove|list <name>", desc: "manage team member list" },
|
|
822
|
+
{ cmd: "hivemind skillify mine-local", desc: "one-shot: mine skills from local sessions (no auth needed)" },
|
|
823
|
+
{ cmd: "hivemind skillify mine-local --n <num|all>", desc: "how many sessions to mine (default: 8)" },
|
|
824
|
+
{ cmd: "hivemind skillify mine-local --force", desc: "re-run even if the manifest sentinel exists" },
|
|
825
|
+
{ cmd: "hivemind skillify mine-local --dry-run", desc: "stop before calling the LLM gate" },
|
|
826
|
+
];
|
|
827
|
+
|
|
828
|
+
function piRenderSkillifyCommands(): string {
|
|
829
|
+
const maxLen = Math.max(...PI_SKILLIFY_COMMANDS.map(c => c.cmd.length));
|
|
830
|
+
return PI_SKILLIFY_COMMANDS
|
|
831
|
+
.map(c => `- ${c.cmd.padEnd(maxLen + 2)} — ${c.desc}`)
|
|
832
|
+
.join("\n");
|
|
833
|
+
}
|
|
834
|
+
|
|
694
835
|
const CONTEXT_PREAMBLE = `DEEPLAKE MEMORY: Persistent memory at ~/.deeplake/memory/ shared across sessions, users, and agents in your org.
|
|
695
836
|
|
|
696
837
|
Three hivemind tools are registered:
|
|
@@ -712,22 +853,7 @@ Organization management — each argument is SEPARATE (do NOT quote subcommands
|
|
|
712
853
|
- hivemind remove <user-id> — remove member
|
|
713
854
|
|
|
714
855
|
SKILLS (skillify) — mine + share reusable skills across the org. Run these in a terminal (or via shell if available):
|
|
715
|
-
|
|
716
|
-
- hivemind skillify pull — sync project skills from the org table
|
|
717
|
-
- hivemind skillify pull --user <email> — only that author's skills
|
|
718
|
-
- hivemind skillify pull --users a,b,c — multiple authors (CSV)
|
|
719
|
-
- hivemind skillify pull --all-users — explicit "no author filter"
|
|
720
|
-
- hivemind skillify pull --to project|global — install location
|
|
721
|
-
- hivemind skillify pull --dry-run — preview only
|
|
722
|
-
- hivemind skillify pull --force — overwrite local (creates .bak)
|
|
723
|
-
- hivemind skillify pull <skill-name> — pull only that skill (combines with --user)
|
|
724
|
-
- hivemind skillify unpull — remove every skill previously installed by pull
|
|
725
|
-
- hivemind skillify unpull --user <email> — remove only that author's pulls
|
|
726
|
-
- hivemind skillify unpull --not-mine — remove all pulls except your own
|
|
727
|
-
- hivemind skillify unpull --dry-run — preview without touching disk
|
|
728
|
-
- hivemind skillify scope <me|team> — sharing scope for new skills
|
|
729
|
-
- hivemind skillify install <project|global> — default install location
|
|
730
|
-
- hivemind skillify team add|remove|list <name> — manage team list`;
|
|
856
|
+
${piRenderSkillifyCommands()}`;
|
|
731
857
|
|
|
732
858
|
export default function hivemindExtension(pi: ExtensionAPI): void {
|
|
733
859
|
const captureEnabled = process.env.HIVEMIND_CAPTURE !== "false";
|
|
@@ -913,10 +1039,22 @@ export default function hivemindExtension(pi: ExtensionAPI): void {
|
|
|
913
1039
|
// per-agent symlink fan-out all live in the worker — no inline
|
|
914
1040
|
// duplicate maintained here.
|
|
915
1041
|
if (creds) runAutopullWorker();
|
|
1042
|
+
else {
|
|
1043
|
+
// First-impression bootstrap: auto-run `hivemind skillify mine-local`
|
|
1044
|
+
// when the user isn't signed in and has Claude Code transcripts on
|
|
1045
|
+
// disk. THIS session sees nothing different; the NEXT pi session
|
|
1046
|
+
// surfaces the mined count + sign-in CTA below.
|
|
1047
|
+
const triggered = piMaybeAutoMineLocal();
|
|
1048
|
+
logHm(`auto-mine: ${triggered ? "triggered" : "skipped"}`);
|
|
1049
|
+
}
|
|
916
1050
|
|
|
1051
|
+
const localMined = piCountLocalManifestEntries();
|
|
1052
|
+
const localMinedNote = localMined > 0
|
|
1053
|
+
? `\n${localMined} local skill${localMined === 1 ? "" : "s"} from past 'hivemind skillify mine-local' run(s) live in ~/.claude/skills/. Run 'hivemind login' to start sharing new mining results with your team.`
|
|
1054
|
+
: "";
|
|
917
1055
|
const additional = creds
|
|
918
1056
|
? `${CONTEXT_PREAMBLE}\nLogged in to Deeplake as org: ${creds.orgName ?? creds.orgId} (workspace: ${creds.workspaceId}).`
|
|
919
|
-
: `${CONTEXT_PREAMBLE}\nNot logged in to Deeplake. Run \`hivemind login\` to authenticate
|
|
1057
|
+
: `${CONTEXT_PREAMBLE}\nNot logged in to Deeplake. Run \`hivemind login\` to authenticate.${localMinedNote}`;
|
|
920
1058
|
return { additionalContext: additional };
|
|
921
1059
|
});
|
|
922
1060
|
|