@deeplake/hivemind 0.7.44 → 0.7.45

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bundle/cli.js CHANGED
@@ -144,23 +144,23 @@ function warn(msg) {
144
144
  function confirm(message, defaultYes = true) {
145
145
  const hint = defaultYes ? "[Y/n]" : "[y/N]";
146
146
  const rl = createInterface({ input: process.stdin, output: process.stderr });
147
- return new Promise((resolve2) => {
147
+ return new Promise((resolve3) => {
148
148
  rl.question(`${message} ${hint} `, (answer) => {
149
149
  rl.close();
150
150
  const a = answer.trim().toLowerCase();
151
151
  if (a === "")
152
- resolve2(defaultYes);
152
+ resolve3(defaultYes);
153
153
  else
154
- resolve2(a === "y" || a === "yes");
154
+ resolve3(a === "y" || a === "yes");
155
155
  });
156
156
  });
157
157
  }
158
158
  function promptLine(message) {
159
159
  const rl = createInterface({ input: process.stdin, output: process.stderr });
160
- return new Promise((resolve2) => {
160
+ return new Promise((resolve3) => {
161
161
  rl.question(message, (answer) => {
162
162
  rl.close();
163
- resolve2(answer.trim());
163
+ resolve3(answer.trim());
164
164
  });
165
165
  });
166
166
  }
@@ -4745,7 +4745,7 @@ function getQueryTimeoutMs() {
4745
4745
  return Number(process.env.HIVEMIND_QUERY_TIMEOUT_MS ?? 1e4);
4746
4746
  }
4747
4747
  function sleep2(ms) {
4748
- return new Promise((resolve2) => setTimeout(resolve2, ms));
4748
+ return new Promise((resolve3) => setTimeout(resolve3, ms));
4749
4749
  }
4750
4750
  function isTimeoutError(error) {
4751
4751
  const name = error instanceof Error ? error.name.toLowerCase() : "";
@@ -4775,7 +4775,7 @@ var Semaphore = class {
4775
4775
  this.active++;
4776
4776
  return;
4777
4777
  }
4778
- await new Promise((resolve2) => this.waiting.push(resolve2));
4778
+ await new Promise((resolve3) => this.waiting.push(resolve3));
4779
4779
  }
4780
4780
  release() {
4781
4781
  this.active--;
@@ -5417,25 +5417,202 @@ if (process.argv[1] && process.argv[1].endsWith("auth-login.js")) {
5417
5417
  });
5418
5418
  }
5419
5419
 
5420
- // dist/src/commands/skillify.js
5421
- import { readdirSync as readdirSync5, existsSync as existsSync26, readFileSync as readFileSync22, mkdirSync as mkdirSync13, renameSync as renameSync7 } from "node:fs";
5422
- import { homedir as homedir19 } from "node:os";
5423
- import { dirname as dirname8, join as join32 } from "node:path";
5420
+ // dist/src/commands/dashboard.js
5421
+ import { mkdirSync as mkdirSync10, writeFileSync as writeFileSync13 } from "node:fs";
5422
+ import { homedir as homedir14 } from "node:os";
5423
+ import { dirname as dirname6, join as join27, resolve as resolve2 } from "node:path";
5424
5424
 
5425
- // dist/src/skillify/scope-config.js
5426
- import { existsSync as existsSync16, mkdirSync as mkdirSync7, readFileSync as readFileSync15, writeFileSync as writeFileSync11 } from "node:fs";
5427
- import { join as join22 } from "node:path";
5425
+ // dist/src/dashboard/data.js
5426
+ import { existsSync as existsSync19, readFileSync as readFileSync18, readdirSync as readdirSync3, statSync as statSync3 } from "node:fs";
5427
+ import { homedir as homedir13 } from "node:os";
5428
+ import { join as join25 } from "node:path";
5429
+
5430
+ // dist/src/notifications/sources/org-stats.js
5431
+ import { existsSync as existsSync15, mkdirSync as mkdirSync7, readFileSync as readFileSync15, writeFileSync as writeFileSync11 } from "node:fs";
5432
+ import { homedir as homedir10 } from "node:os";
5433
+ import { dirname as dirname3, join as join20 } from "node:path";
5434
+ var log5 = (msg) => log2("notifications-org-stats", msg);
5435
+ var FETCH_TIMEOUT_MS = 1500;
5436
+ var DEFAULT_API_URL3 = "https://api.deeplake.ai";
5437
+ var CACHE_TTL_MS = 60 * 60 * 1e3;
5438
+ function cacheFilePath() {
5439
+ return join20(homedir10(), ".deeplake", "hivemind-stats-cache.json");
5440
+ }
5441
+ function cacheScopeKey(creds) {
5442
+ return JSON.stringify({
5443
+ apiUrl: creds.apiUrl ?? DEFAULT_API_URL3,
5444
+ orgId: creds.orgId ?? "",
5445
+ userName: creds.userName ?? ""
5446
+ });
5447
+ }
5448
+ function scopeFromServer(s) {
5449
+ const n = (v) => typeof v === "number" && Number.isFinite(v) && v >= 0 ? v : 0;
5450
+ return {
5451
+ sessionsCount: n(s?.sessions_count),
5452
+ memoryRecallCount: n(s?.memory_recall_count),
5453
+ memorySearchBytes: n(s?.memory_search_bytes)
5454
+ };
5455
+ }
5456
+ function readCache(scopeKey) {
5457
+ if (!existsSync15(cacheFilePath()))
5458
+ return {};
5459
+ try {
5460
+ const parsed = JSON.parse(readFileSync15(cacheFilePath(), "utf-8"));
5461
+ if (!parsed || typeof parsed !== "object")
5462
+ return {};
5463
+ if (parsed.scopeKey !== scopeKey)
5464
+ return {};
5465
+ if (typeof parsed.fetchedAt !== "number")
5466
+ return {};
5467
+ const age = Date.now() - parsed.fetchedAt;
5468
+ const data = parsed.data;
5469
+ if (!data || typeof data !== "object" || !data.org || !data.user)
5470
+ return {};
5471
+ if (age >= 0 && age < CACHE_TTL_MS)
5472
+ return { fresh: data };
5473
+ return { stale: data };
5474
+ } catch (e) {
5475
+ log5(`cache read failed: ${e?.message ?? String(e)}`);
5476
+ return {};
5477
+ }
5478
+ }
5479
+ function writeCache(scopeKey, data) {
5480
+ try {
5481
+ mkdirSync7(dirname3(cacheFilePath()), { recursive: true });
5482
+ const body = { fetchedAt: Date.now(), scopeKey, data };
5483
+ writeFileSync11(cacheFilePath(), JSON.stringify(body), "utf-8");
5484
+ } catch (e) {
5485
+ log5(`cache write failed: ${e?.message ?? String(e)}`);
5486
+ }
5487
+ }
5488
+ async function fetchOrgStats(creds) {
5489
+ if (!creds?.token)
5490
+ return null;
5491
+ const apiUrl = creds.apiUrl ?? DEFAULT_API_URL3;
5492
+ const scopeKey = cacheScopeKey(creds);
5493
+ const { fresh, stale } = readCache(scopeKey);
5494
+ if (fresh) {
5495
+ log5("cache hit \u2014 returning fresh org stats");
5496
+ return fresh;
5497
+ }
5498
+ const url = `${apiUrl}/me/hivemind-stats`;
5499
+ const ctrl = new AbortController();
5500
+ const timeoutHandle = setTimeout(() => ctrl.abort(), FETCH_TIMEOUT_MS);
5501
+ try {
5502
+ const resp = await fetch(url, {
5503
+ headers: {
5504
+ Authorization: `Bearer ${creds.token}`,
5505
+ ...creds.orgId ? { "X-Activeloop-Org-Id": creds.orgId } : {}
5506
+ },
5507
+ signal: ctrl.signal
5508
+ });
5509
+ if (!resp.ok) {
5510
+ log5(`fetch ${url} returned ${resp.status}`);
5511
+ return stale ?? null;
5512
+ }
5513
+ const body = await resp.json();
5514
+ if (!body || typeof body !== "object") {
5515
+ log5(`fetch ${url} returned malformed body`);
5516
+ return stale ?? null;
5517
+ }
5518
+ const data = {
5519
+ org: scopeFromServer(body.org),
5520
+ user: scopeFromServer(body.user)
5521
+ };
5522
+ writeCache(scopeKey, data);
5523
+ log5(`fetched org stats from ${apiUrl}`);
5524
+ return data;
5525
+ } catch (e) {
5526
+ log5(`fetch ${url} failed: ${e?.message ?? String(e)}`);
5527
+ return stale ?? null;
5528
+ } finally {
5529
+ clearTimeout(timeoutHandle);
5530
+ }
5531
+ }
5532
+
5533
+ // dist/src/notifications/usage-tracker.js
5534
+ import { appendFileSync as appendFileSync2, existsSync as existsSync16, mkdirSync as mkdirSync8, readFileSync as readFileSync16, readdirSync as readdirSync2 } from "node:fs";
5535
+ import { dirname as dirname4, join as join21 } from "node:path";
5536
+ import { homedir as homedir11 } from "node:os";
5537
+ var log6 = (msg) => log2("usage-tracker", msg);
5538
+ function statsFilePath() {
5539
+ return join21(homedir11(), ".deeplake", "usage-stats.jsonl");
5540
+ }
5541
+ function readUsageRecords() {
5542
+ try {
5543
+ if (!existsSync16(statsFilePath()))
5544
+ return [];
5545
+ const raw = readFileSync16(statsFilePath(), "utf-8");
5546
+ const out = [];
5547
+ for (const line of raw.split("\n")) {
5548
+ const trimmed = line.trim();
5549
+ if (!trimmed)
5550
+ continue;
5551
+ try {
5552
+ const rec = JSON.parse(trimmed);
5553
+ if (typeof rec.endedAt === "string" && typeof rec.sessionId === "string") {
5554
+ out.push({
5555
+ endedAt: rec.endedAt,
5556
+ sessionId: rec.sessionId,
5557
+ memorySearchBytes: typeof rec.memorySearchBytes === "number" ? rec.memorySearchBytes : 0,
5558
+ memorySearchCount: typeof rec.memorySearchCount === "number" ? rec.memorySearchCount : 0
5559
+ });
5560
+ }
5561
+ } catch {
5562
+ }
5563
+ }
5564
+ return out;
5565
+ } catch (e) {
5566
+ log6(`readUsageRecords failed: ${e?.message ?? String(e)}`);
5567
+ return [];
5568
+ }
5569
+ }
5570
+ function sumMetric(records, key) {
5571
+ let total = 0;
5572
+ for (const r of records) {
5573
+ const v = r[key];
5574
+ if (typeof v === "number" && Number.isFinite(v))
5575
+ total += v;
5576
+ }
5577
+ return total;
5578
+ }
5579
+ function countUserGeneratedSkills(userName) {
5580
+ if (!userName)
5581
+ return 0;
5582
+ const dir = join21(homedir11(), ".claude", "skills");
5583
+ if (!existsSync16(dir))
5584
+ return 0;
5585
+ const suffix = `--${userName}`;
5586
+ try {
5587
+ let count = 0;
5588
+ for (const name of readdirSync2(dir)) {
5589
+ const idx = name.lastIndexOf(suffix);
5590
+ if (idx > 0 && idx + suffix.length === name.length)
5591
+ count += 1;
5592
+ }
5593
+ return count;
5594
+ } catch (e) {
5595
+ log6(`countUserGeneratedSkills readdir failed: ${e?.message ?? String(e)}`);
5596
+ return 0;
5597
+ }
5598
+ }
5599
+
5600
+ // dist/src/skillify/state.js
5601
+ import { readFileSync as readFileSync17, writeFileSync as writeFileSync12, writeSync, mkdirSync as mkdirSync9, renameSync as renameSync5, rmdirSync, existsSync as existsSync18, lstatSync as lstatSync3, unlinkSync as unlinkSync8, openSync as openSync2, closeSync as closeSync2 } from "node:fs";
5602
+ import { execSync as execSync2 } from "node:child_process";
5603
+ import { createHash } from "node:crypto";
5604
+ import { join as join24, basename } from "node:path";
5428
5605
 
5429
5606
  // dist/src/skillify/legacy-migration.js
5430
- import { existsSync as existsSync15, renameSync as renameSync4 } from "node:fs";
5431
- import { dirname as dirname3, join as join21 } from "node:path";
5607
+ import { existsSync as existsSync17, renameSync as renameSync4 } from "node:fs";
5608
+ import { dirname as dirname5, join as join23 } from "node:path";
5432
5609
 
5433
5610
  // dist/src/skillify/state-dir.js
5434
- import { homedir as homedir10 } from "node:os";
5435
- import { join as join20 } from "node:path";
5611
+ import { homedir as homedir12 } from "node:os";
5612
+ import { join as join22 } from "node:path";
5436
5613
  function getStateDir() {
5437
5614
  const override = process.env.HIVEMIND_STATE_DIR?.trim();
5438
- return override && override.length > 0 ? override : join20(homedir10(), ".deeplake", "state", "skillify");
5615
+ return override && override.length > 0 ? override : join22(homedir12(), ".deeplake", "state", "skillify");
5439
5616
  }
5440
5617
 
5441
5618
  // dist/src/skillify/legacy-migration.js
@@ -5448,10 +5625,10 @@ function migrateLegacyStateDir() {
5448
5625
  return;
5449
5626
  attempted = true;
5450
5627
  const current = getStateDir();
5451
- const legacy = join21(dirname3(current), "skilify");
5452
- if (!existsSync15(legacy))
5628
+ const legacy = join23(dirname5(current), "skilify");
5629
+ if (!existsSync17(legacy))
5453
5630
  return;
5454
- if (existsSync15(current))
5631
+ if (existsSync17(current))
5455
5632
  return;
5456
5633
  try {
5457
5634
  renameSync4(legacy, current);
@@ -5466,18 +5643,859 @@ function migrateLegacyStateDir() {
5466
5643
  }
5467
5644
  }
5468
5645
 
5646
+ // dist/src/skillify/state.js
5647
+ var YIELD_BUF = new Int32Array(new SharedArrayBuffer(4));
5648
+ var TRIGGER_THRESHOLD = (() => {
5649
+ const n = Number(process.env.HIVEMIND_SKILLIFY_EVERY_N_TURNS ?? "");
5650
+ return Number.isInteger(n) && n > 0 ? n : 20;
5651
+ })();
5652
+ var DEFAULT_PORTS = {
5653
+ http: "80",
5654
+ https: "443",
5655
+ ssh: "22",
5656
+ git: "9418"
5657
+ };
5658
+ function normalizeGitRemoteUrl(url) {
5659
+ let s = url.trim();
5660
+ const schemeMatch = s.match(/^([a-z][a-z0-9+.-]*):\/\//i);
5661
+ const scheme = schemeMatch ? schemeMatch[1].toLowerCase() : null;
5662
+ if (schemeMatch)
5663
+ s = s.slice(schemeMatch[0].length);
5664
+ if (!scheme) {
5665
+ const scp = s.match(/^(?:[^@/\s]+@)?([^:/\s]+):(.+)$/);
5666
+ if (scp)
5667
+ s = `${scp[1]}/${scp[2]}`;
5668
+ }
5669
+ s = s.replace(/^[^@/]+@/, "");
5670
+ if (scheme && DEFAULT_PORTS[scheme]) {
5671
+ s = s.replace(new RegExp(`^([^/]+):${DEFAULT_PORTS[scheme]}(/|$)`), "$1$2");
5672
+ }
5673
+ s = s.replace(/\.git\/?$/i, "");
5674
+ s = s.replace(/\/+$/, "");
5675
+ return s.toLowerCase();
5676
+ }
5677
+ function deriveProjectKey(cwd) {
5678
+ const project = basename(cwd) || "unknown";
5679
+ let signature = null;
5680
+ try {
5681
+ const raw = execSync2("git config --get remote.origin.url", {
5682
+ cwd,
5683
+ encoding: "utf-8",
5684
+ stdio: ["ignore", "pipe", "ignore"]
5685
+ }).trim();
5686
+ signature = raw ? normalizeGitRemoteUrl(raw) : null;
5687
+ } catch {
5688
+ }
5689
+ const input = signature ?? cwd;
5690
+ const key = createHash("sha1").update(input).digest("hex").slice(0, 16);
5691
+ return { key, project };
5692
+ }
5693
+
5694
+ // dist/src/dashboard/data.js
5695
+ var log7 = (msg) => log2("dashboard-data", msg);
5696
+ var BYTES_PER_TOKEN = 4;
5697
+ var SAVINGS_MULTIPLIER = 1.7;
5698
+ function graphsRoot() {
5699
+ return process.env.HIVEMIND_GRAPHS_HOME ?? join25(homedir13(), ".hivemind", "graphs");
5700
+ }
5701
+ function bytesToSavedTokens(bytes) {
5702
+ if (!Number.isFinite(bytes) || bytes <= 0)
5703
+ return 0;
5704
+ const delivered = bytes / BYTES_PER_TOKEN;
5705
+ return (SAVINGS_MULTIPLIER - 1) * delivered;
5706
+ }
5707
+ function resolveSnapshot(repoDir) {
5708
+ const snapshotsDir = join25(repoDir, "snapshots");
5709
+ if (!existsSync19(snapshotsDir))
5710
+ return null;
5711
+ let snapshotPath = null;
5712
+ const pointer = join25(repoDir, "latest-commit.txt");
5713
+ if (existsSync19(pointer)) {
5714
+ try {
5715
+ const sha = readFileSync18(pointer, "utf-8").trim();
5716
+ if (sha) {
5717
+ const candidate = join25(snapshotsDir, `${sha}.json`);
5718
+ if (existsSync19(candidate))
5719
+ snapshotPath = candidate;
5720
+ else
5721
+ log7(`latest-commit.txt points at missing ${sha}.json \u2014 scanning snapshots/`);
5722
+ }
5723
+ } catch (e) {
5724
+ log7(`latest-commit.txt read failed: ${e?.message ?? String(e)}`);
5725
+ }
5726
+ }
5727
+ if (!snapshotPath) {
5728
+ try {
5729
+ const candidates = readdirSync3(snapshotsDir).filter((name) => name.endsWith(".json")).map((name) => {
5730
+ const full = join25(snapshotsDir, name);
5731
+ return { full, mtime: statSync3(full).mtimeMs };
5732
+ }).sort((a, b) => b.mtime - a.mtime);
5733
+ if (candidates.length > 0)
5734
+ snapshotPath = candidates[0].full;
5735
+ } catch (e) {
5736
+ log7(`snapshots/ scan failed: ${e?.message ?? String(e)}`);
5737
+ }
5738
+ }
5739
+ if (!snapshotPath)
5740
+ return null;
5741
+ let raw;
5742
+ try {
5743
+ raw = readFileSync18(snapshotPath, "utf-8");
5744
+ } catch (e) {
5745
+ log7(`snapshot read failed: ${e?.message ?? String(e)}`);
5746
+ return null;
5747
+ }
5748
+ let parsed;
5749
+ try {
5750
+ parsed = JSON.parse(raw);
5751
+ } catch (e) {
5752
+ log7(`snapshot parse failed: ${e?.message ?? String(e)}`);
5753
+ return null;
5754
+ }
5755
+ if (!parsed || typeof parsed !== "object" || !Array.isArray(parsed.nodes) || !Array.isArray(parsed.links)) {
5756
+ log7("snapshot shape invalid (missing nodes/links arrays)");
5757
+ return null;
5758
+ }
5759
+ return {
5760
+ commitSha: parsed.graph?.commit_sha ?? null,
5761
+ snapshotPath,
5762
+ nodeCount: parsed.nodes.length,
5763
+ edgeCount: parsed.links.length,
5764
+ snapshot: parsed
5765
+ };
5766
+ }
5767
+ async function loadKpis(creds) {
5768
+ const userName = creds?.userName;
5769
+ const skillsCreated = countUserGeneratedSkills(userName);
5770
+ const records = readUsageRecords();
5771
+ const localBytes = sumMetric(records, "memorySearchBytes");
5772
+ const localCount = sumMetric(records, "memorySearchCount");
5773
+ let orgStats = null;
5774
+ if (creds?.token) {
5775
+ try {
5776
+ orgStats = await fetchOrgStats(creds);
5777
+ } catch (e) {
5778
+ log7(`fetchOrgStats threw: ${e?.message ?? String(e)}`);
5779
+ }
5780
+ }
5781
+ if (orgStats) {
5782
+ return {
5783
+ tokensSaved: bytesToSavedTokens(orgStats.org.memorySearchBytes),
5784
+ tokensSource: "org",
5785
+ skillsCreated,
5786
+ memorySearches: orgStats.org.memoryRecallCount,
5787
+ sessionsCount: orgStats.org.sessionsCount,
5788
+ userTokensSaved: bytesToSavedTokens(orgStats.user.memorySearchBytes)
5789
+ };
5790
+ }
5791
+ if (records.length > 0) {
5792
+ return {
5793
+ tokensSaved: bytesToSavedTokens(localBytes),
5794
+ tokensSource: "local",
5795
+ skillsCreated,
5796
+ memorySearches: localCount,
5797
+ sessionsCount: records.length,
5798
+ userTokensSaved: bytesToSavedTokens(localBytes)
5799
+ };
5800
+ }
5801
+ return {
5802
+ tokensSaved: null,
5803
+ tokensSource: "none",
5804
+ skillsCreated,
5805
+ memorySearches: 0,
5806
+ sessionsCount: null,
5807
+ userTokensSaved: null
5808
+ };
5809
+ }
5810
+ async function loadDashboardData(opts = {}) {
5811
+ const cwd = opts.cwd ?? process.cwd();
5812
+ const { key: repoKey, project: repoProject } = deriveProjectKey(cwd);
5813
+ const repoDir = join25(opts.graphsHome ?? graphsRoot(), repoKey);
5814
+ const graph = resolveSnapshot(repoDir);
5815
+ const creds = opts.creds === void 0 ? loadCredentials() : opts.creds;
5816
+ const kpis = await loadKpis(creds);
5817
+ return {
5818
+ repoKey,
5819
+ repoProject,
5820
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
5821
+ kpis,
5822
+ graph
5823
+ };
5824
+ }
5825
+
5826
+ // dist/src/dashboard/open.js
5827
+ import { spawn } from "node:child_process";
5828
+ import { accessSync, constants as fsConstants, statSync as statSync4 } from "node:fs";
5829
+ import { platform as nodePlatform } from "node:os";
5830
+ import { delimiter, join as join26 } from "node:path";
5831
+ function resolveOpenPlatform() {
5832
+ const p = nodePlatform();
5833
+ if (p === "linux" || p === "darwin" || p === "win32")
5834
+ return p;
5835
+ return null;
5836
+ }
5837
+ function openCommandFor(p, path) {
5838
+ switch (p) {
5839
+ case "linux":
5840
+ return { command: "xdg-open", args: [path] };
5841
+ case "darwin":
5842
+ return { command: "open", args: [path] };
5843
+ case "win32":
5844
+ return { command: "cmd", args: ["/c", "start", "", path] };
5845
+ }
5846
+ }
5847
+ function findBinaryOnPath(name) {
5848
+ const PATH = process.env.PATH ?? "";
5849
+ if (!PATH)
5850
+ return null;
5851
+ const isWin = nodePlatform() === "win32";
5852
+ const exts = isWin ? (process.env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD").split(";").map((e) => e.trim()).filter(Boolean) : [""];
5853
+ for (const dir of PATH.split(delimiter)) {
5854
+ if (!dir)
5855
+ continue;
5856
+ for (const ext of exts) {
5857
+ const candidate = join26(dir, name + ext);
5858
+ try {
5859
+ const st = statSync4(candidate);
5860
+ if (!st.isFile())
5861
+ continue;
5862
+ if (isWin)
5863
+ return candidate;
5864
+ try {
5865
+ accessSync(candidate, fsConstants.X_OK);
5866
+ return candidate;
5867
+ } catch {
5868
+ }
5869
+ } catch {
5870
+ }
5871
+ }
5872
+ }
5873
+ return null;
5874
+ }
5875
+ function openInBrowser(path, opts = {}) {
5876
+ const p = opts.platformOverride === void 0 ? resolveOpenPlatform() : opts.platformOverride;
5877
+ if (!p)
5878
+ return { attempted: false };
5879
+ const { command, args } = openCommandFor(p, path);
5880
+ const exists = opts.binaryExists ?? ((cmd) => findBinaryOnPath(cmd) !== null);
5881
+ if (!exists(command))
5882
+ return { attempted: false };
5883
+ const useSpawn = opts.spawner ?? spawn;
5884
+ try {
5885
+ const child = useSpawn(command, args, { stdio: "ignore", detached: true });
5886
+ child.on("error", () => {
5887
+ });
5888
+ if (typeof child.unref === "function") {
5889
+ child.unref();
5890
+ }
5891
+ return { attempted: true, command };
5892
+ } catch {
5893
+ return { attempted: false };
5894
+ }
5895
+ }
5896
+
5897
+ // dist/src/dashboard/render.js
5898
+ var VIS_NETWORK_CDN = "https://unpkg.com/vis-network@9.1.9/standalone/umd/vis-network.min.js";
5899
+ var KIND_COLORS = {
5900
+ function: "#7aa2f7",
5901
+ // soft blue
5902
+ class: "#bb9af7",
5903
+ // purple
5904
+ method: "#9ece6a",
5905
+ // green
5906
+ interface: "#e0af68",
5907
+ // amber
5908
+ type_alias: "#7dcfff",
5909
+ // cyan
5910
+ enum: "#f7768e",
5911
+ // pink
5912
+ const: "#9d7cd8",
5913
+ // muted purple
5914
+ module: "#565f89"
5915
+ // slate
5916
+ };
5917
+ var DEFAULT_NODE_COLOR = "#565f89";
5918
+ function isObject2(v) {
5919
+ return v !== null && typeof v === "object" && !Array.isArray(v);
5920
+ }
5921
+ function asString(v) {
5922
+ return typeof v === "string" ? v : null;
5923
+ }
5924
+ function transformSnapshotToVis(snapshot) {
5925
+ if (!isObject2(snapshot))
5926
+ return { nodes: [], edges: [] };
5927
+ const raw = snapshot;
5928
+ const visNodes = [];
5929
+ const ids = /* @__PURE__ */ new Set();
5930
+ if (Array.isArray(raw.nodes)) {
5931
+ for (const n of raw.nodes) {
5932
+ if (!isObject2(n))
5933
+ continue;
5934
+ const node = n;
5935
+ const id = asString(node.id);
5936
+ if (!id)
5937
+ continue;
5938
+ if (ids.has(id))
5939
+ continue;
5940
+ ids.add(id);
5941
+ const label = asString(node.label) ?? id;
5942
+ const kind = asString(node.kind);
5943
+ const sourceFile = asString(node.source_file);
5944
+ const sourceLoc = asString(node.source_location);
5945
+ const titleParts = [];
5946
+ if (kind)
5947
+ titleParts.push(kind);
5948
+ if (sourceFile) {
5949
+ const loc = sourceLoc ? `${sourceFile}:${sourceLoc}` : sourceFile;
5950
+ titleParts.push(loc);
5951
+ }
5952
+ const color = kind && KIND_COLORS[kind] ? KIND_COLORS[kind] : DEFAULT_NODE_COLOR;
5953
+ visNodes.push({
5954
+ id,
5955
+ label,
5956
+ title: titleParts.length > 0 ? titleParts.map(escHtml).join(" \xB7 ") : escHtml(id),
5957
+ group: kind ?? void 0,
5958
+ color: { background: color, border: color }
5959
+ });
5960
+ }
5961
+ }
5962
+ const visEdges = [];
5963
+ if (Array.isArray(raw.links)) {
5964
+ for (const l of raw.links) {
5965
+ if (!isObject2(l))
5966
+ continue;
5967
+ const edge = l;
5968
+ const from = asString(edge.source);
5969
+ const to = asString(edge.target);
5970
+ if (!from || !to)
5971
+ continue;
5972
+ const relation = asString(edge.relation);
5973
+ const confidence = asString(edge.confidence);
5974
+ const titleParts = [];
5975
+ if (relation)
5976
+ titleParts.push(relation);
5977
+ if (confidence)
5978
+ titleParts.push(`[${confidence}]`);
5979
+ visEdges.push({
5980
+ from,
5981
+ to,
5982
+ title: titleParts.length > 0 ? titleParts.map(escHtml).join(" ") : `${escHtml(from)} \u2192 ${escHtml(to)}`
5983
+ });
5984
+ }
5985
+ }
5986
+ return { nodes: visNodes, edges: visEdges };
5987
+ }
5988
+ function escHtml(s) {
5989
+ return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
5990
+ }
5991
+ function safeJsonForScript(value) {
5992
+ return JSON.stringify(value).replace(/<\//g, "<\\/").replace(/<!--/g, "<\\u0021--").replace(/-->/g, "--\\u003e");
5993
+ }
5994
+ function formatTokensCompact(n) {
5995
+ if (!Number.isFinite(n) || n <= 0)
5996
+ return "0";
5997
+ if (n < 1e3)
5998
+ return `${Math.round(n)}`;
5999
+ if (n < 1e5)
6000
+ return `${(n / 1e3).toFixed(1)}k`;
6001
+ if (n < 1e6)
6002
+ return `${Math.round(n / 1e3)}k`;
6003
+ return `${(n / 1e6).toFixed(1)}M`;
6004
+ }
6005
+ function formatInt(n) {
6006
+ if (!Number.isFinite(n))
6007
+ return "0";
6008
+ return Math.round(n).toLocaleString("en-US");
6009
+ }
6010
+ function renderKpiCards(kpis) {
6011
+ const tokensValue = kpis.tokensSaved == null ? "\u2014" : `~${formatTokensCompact(kpis.tokensSaved)}`;
6012
+ const tokensSub = (() => {
6013
+ if (kpis.tokensSource === "org") {
6014
+ return kpis.userTokensSaved != null ? `Org-wide \xB7 you ~${formatTokensCompact(kpis.userTokensSaved)}` : "Org-wide";
6015
+ }
6016
+ if (kpis.tokensSource === "local")
6017
+ return "Local (this machine)";
6018
+ return "Run a session to start tracking";
6019
+ })();
6020
+ const memoryValue = kpis.memorySearches > 0 ? formatInt(kpis.memorySearches) : kpis.tokensSource === "none" ? "\u2014" : "0";
6021
+ const sessionsValue = kpis.sessionsCount == null ? "\u2014" : formatInt(kpis.sessionsCount);
6022
+ const cards = [
6023
+ {
6024
+ label: "Tokens saved",
6025
+ value: tokensValue,
6026
+ sub: tokensSub
6027
+ },
6028
+ {
6029
+ label: "Skills created",
6030
+ value: formatInt(kpis.skillsCreated),
6031
+ sub: "~/.claude/skills/"
6032
+ },
6033
+ {
6034
+ label: "Memory recalls",
6035
+ value: memoryValue,
6036
+ sub: kpis.tokensSource === "org" ? "Org-wide" : kpis.tokensSource === "local" ? "Local" : ""
6037
+ },
6038
+ {
6039
+ label: "Sessions",
6040
+ value: sessionsValue,
6041
+ sub: kpis.tokensSource === "org" ? "Org-wide" : kpis.tokensSource === "local" ? "Local" : ""
6042
+ }
6043
+ ];
6044
+ return cards.map((c) => `
6045
+ <div class="kpi">
6046
+ <div class="kpi-label">${escHtml(c.label)}</div>
6047
+ <div class="kpi-value">${escHtml(c.value)}</div>
6048
+ <div class="kpi-sub">${escHtml(c.sub)}</div>
6049
+ </div>`).join("");
6050
+ }
6051
+ function renderGraphSection(data) {
6052
+ if (data.graph == null) {
6053
+ return `
6054
+ <div class="graph-card">
6055
+ <h2>Codebase graph</h2>
6056
+ <div class="empty">
6057
+ No graph snapshot yet for this repo.<br>
6058
+ Run <code>hivemind graph build</code> to generate one.
6059
+ </div>
6060
+ </div>`;
6061
+ }
6062
+ const visPayload = transformSnapshotToVis(data.graph.snapshot);
6063
+ const commitLabel = data.graph.commitSha ? `commit ${data.graph.commitSha.slice(0, 12)}` : "no commit (loose dir)";
6064
+ const meta = `${formatInt(data.graph.nodeCount)} nodes \xB7 ${formatInt(data.graph.edgeCount)} edges \xB7 ${commitLabel}`;
6065
+ return `
6066
+ <div class="graph-card">
6067
+ <h2>Codebase graph</h2>
6068
+ <div class="graph-meta">${escHtml(meta)}</div>
6069
+ <div id="graph"></div>
6070
+ </div>
6071
+ <script type="application/json" id="hm-graph-data">${safeJsonForScript(visPayload)}</script>
6072
+ <script src="${VIS_NETWORK_CDN}"></script>
6073
+ <script>
6074
+ (function () {
6075
+ var holder = document.getElementById('hm-graph-data');
6076
+ var container = document.getElementById('graph');
6077
+ if (!holder || !container || typeof vis === 'undefined') return;
6078
+ var payload;
6079
+ try { payload = JSON.parse(holder.textContent); }
6080
+ catch (e) { container.textContent = 'graph payload parse failed'; return; }
6081
+ if (!payload || !Array.isArray(payload.nodes) || payload.nodes.length === 0) {
6082
+ container.textContent = 'snapshot has no nodes';
6083
+ return;
6084
+ }
6085
+ new vis.Network(container, payload, {
6086
+ nodes: {
6087
+ shape: 'dot',
6088
+ size: 9,
6089
+ font: { color: '#e8eaed', size: 11, face: 'system-ui, sans-serif' },
6090
+ borderWidth: 1,
6091
+ },
6092
+ edges: {
6093
+ color: { color: 'rgba(120, 130, 150, 0.45)', highlight: '#f5b80a', hover: '#e8eaed' },
6094
+ arrows: { to: { enabled: true, scaleFactor: 0.45 } },
6095
+ smooth: { enabled: true, type: 'continuous', roundness: 0.2 },
6096
+ width: 1,
6097
+ },
6098
+ physics: {
6099
+ stabilization: { iterations: 120 },
6100
+ barnesHut: { gravitationalConstant: -2200, springLength: 80, springConstant: 0.04 },
6101
+ },
6102
+ interaction: { hover: true, dragNodes: true, tooltipDelay: 120 },
6103
+ });
6104
+ }());
6105
+ </script>`;
6106
+ }
6107
+ var STYLES = `
6108
+ :root {
6109
+ color-scheme: dark;
6110
+ --bg: #0b0d10;
6111
+ --fg: #e8eaed;
6112
+ --muted: #8b9099;
6113
+ --accent: #f5b80a;
6114
+ --card: #15181d;
6115
+ --border: #22272e;
6116
+ }
6117
+ * { box-sizing: border-box; }
6118
+ html, body { margin: 0; padding: 0; }
6119
+ body {
6120
+ font: 14px/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
6121
+ background: var(--bg);
6122
+ color: var(--fg);
6123
+ padding: 24px;
6124
+ }
6125
+ .header { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 24px; gap: 16px; flex-wrap: wrap; }
6126
+ .brand { font-weight: 600; font-size: 18px; }
6127
+ .brand .bee { color: var(--accent); margin-right: 4px; }
6128
+ .brand .repo { color: var(--muted); font-weight: 400; margin-left: 8px; }
6129
+ .header .ts { color: var(--muted); font-size: 12px; }
6130
+ .kpi-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 16px; margin-bottom: 32px; }
6131
+ .kpi { background: var(--card); border: 1px solid var(--border); border-radius: 8px; padding: 16px 20px; }
6132
+ .kpi-label { color: var(--muted); font-size: 11px; text-transform: uppercase; letter-spacing: 0.06em; }
6133
+ .kpi-value { font-size: 28px; font-weight: 600; margin-top: 6px; line-height: 1.1; }
6134
+ .kpi-sub { color: var(--muted); font-size: 12px; margin-top: 4px; }
6135
+ .graph-card { background: var(--card); border: 1px solid var(--border); border-radius: 8px; padding: 16px; }
6136
+ .graph-card h2 { margin: 0 0 8px; font-size: 15px; font-weight: 500; }
6137
+ .graph-meta { color: var(--muted); font-size: 12px; margin-bottom: 12px; }
6138
+ #graph { height: 70vh; border: 1px solid var(--border); border-radius: 4px; background: #0e1116; }
6139
+ .empty { padding: 48px 16px; text-align: center; color: var(--muted); }
6140
+ .empty code { background: #1c2128; padding: 2px 6px; border-radius: 3px; color: var(--fg); font-family: ui-monospace, "SFMono-Regular", monospace; }
6141
+ .footer { color: var(--muted); font-size: 11px; margin-top: 24px; text-align: right; }
6142
+ `;
6143
+ function renderDashboardHtml(data) {
6144
+ const title = `Hivemind Dashboard \xB7 ${data.repoProject}`;
6145
+ return `<!DOCTYPE html>
6146
+ <html lang="en">
6147
+ <head>
6148
+ <meta charset="utf-8">
6149
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6150
+ <title>${escHtml(title)}</title>
6151
+ <style>${STYLES}</style>
6152
+ </head>
6153
+ <body>
6154
+ <div class="header">
6155
+ <div class="brand">
6156
+ <span class="bee">\u{1F41D}</span>hivemind dashboard
6157
+ <span class="repo">/ ${escHtml(data.repoProject)}</span>
6158
+ </div>
6159
+ <div class="ts">${escHtml(data.generatedAt)}</div>
6160
+ </div>
6161
+ <div class="kpi-grid">${renderKpiCards(data.kpis)}
6162
+ </div>
6163
+ ${renderGraphSection(data)}
6164
+ <div class="footer">repo_key ${escHtml(data.repoKey)}</div>
6165
+ </body>
6166
+ </html>
6167
+ `;
6168
+ }
6169
+
6170
+ // dist/src/dashboard/serve.js
6171
+ import { createServer } from "node:http";
6172
+ var DEFAULT_PORT = 8123;
6173
+ var DEFAULT_HOST = "127.0.0.1";
6174
+ function handleRequest(html) {
6175
+ return (req, res) => {
6176
+ const url = req.url ?? "/";
6177
+ const path = url.split("?")[0];
6178
+ if (req.method === "GET" && (path === "/" || path === "/index.html")) {
6179
+ res.statusCode = 200;
6180
+ res.setHeader("Content-Type", "text/html; charset=utf-8");
6181
+ res.setHeader("Cache-Control", "no-store");
6182
+ res.end(html);
6183
+ return;
6184
+ }
6185
+ if (req.method === "GET" && path === "/health") {
6186
+ res.statusCode = 204;
6187
+ res.end();
6188
+ return;
6189
+ }
6190
+ res.statusCode = 404;
6191
+ res.setHeader("Content-Type", "text/plain; charset=utf-8");
6192
+ res.end("Not found. The dashboard lives at /.\n");
6193
+ };
6194
+ }
6195
+ function tryListen(server, host, port) {
6196
+ return new Promise((resolve3, reject) => {
6197
+ const onError = (err) => {
6198
+ server.off("listening", onListening);
6199
+ reject(err);
6200
+ };
6201
+ const onListening = () => {
6202
+ server.off("error", onError);
6203
+ const addr = server.address();
6204
+ if (!addr || typeof addr === "string") {
6205
+ reject(new Error("server bound to a non-IP address"));
6206
+ return;
6207
+ }
6208
+ resolve3(addr.port);
6209
+ };
6210
+ server.once("error", onError);
6211
+ server.once("listening", onListening);
6212
+ server.listen(port, host);
6213
+ });
6214
+ }
6215
+ async function serveDashboardHtml(opts) {
6216
+ const host = opts.host ?? DEFAULT_HOST;
6217
+ const requested = opts.port === void 0 || !Number.isFinite(opts.port) || opts.port < 0 ? DEFAULT_PORT : opts.port;
6218
+ const server = createServer(handleRequest(opts.html));
6219
+ let bound;
6220
+ try {
6221
+ bound = await tryListen(server, host, requested);
6222
+ } catch (e) {
6223
+ if (e?.code !== "EADDRINUSE")
6224
+ throw e;
6225
+ const fallback = createServer(handleRequest(opts.html));
6226
+ bound = await tryListen(fallback, host, 0);
6227
+ server.removeAllListeners();
6228
+ return makeHandle(fallback, host, bound);
6229
+ }
6230
+ return makeHandle(server, host, bound);
6231
+ }
6232
+ function makeHandle(server, host, port) {
6233
+ let resolveStopped;
6234
+ const stopped = new Promise((resolve3) => {
6235
+ resolveStopped = resolve3;
6236
+ });
6237
+ server.on("close", () => resolveStopped());
6238
+ return {
6239
+ host,
6240
+ port,
6241
+ stopped,
6242
+ close: () => new Promise((resolve3, reject) => {
6243
+ server.close((err) => err ? reject(err) : resolve3());
6244
+ })
6245
+ };
6246
+ }
6247
+
6248
+ // dist/src/commands/dashboard.js
6249
+ var USAGE = `hivemind dashboard \u2014 codebase graph + KPI dashboard (HTML)
6250
+
6251
+ Usage:
6252
+ hivemind dashboard [--cwd <path>] [--out <path>] [--no-open]
6253
+ [--serve] [--port <n>]
6254
+ Build a self-contained HTML dashboard for this repo, write it
6255
+ to disk, and either open it in the default browser or serve
6256
+ it over loopback HTTP for headless / SSH workflows.
6257
+
6258
+ --cwd <path> Use a different project root (defaults to cwd).
6259
+ --out <path> Write to a custom path (defaults to
6260
+ ~/.hivemind/dashboards/<repo-key>/index.html).
6261
+ --no-open Don't open the browser. Combine with --serve
6262
+ to start the server without auto-launching.
6263
+ --serve Start a loopback HTTP server (127.0.0.1) so the
6264
+ dashboard is reachable at a URL. Stays alive
6265
+ until Ctrl+C. Ideal for VS Code / Cursor
6266
+ Remote-SSH (auto-forwards the port \u2192 click to
6267
+ open in the integrated browser tab).
6268
+ --port <n> Port for --serve (default 8123). Falls back to
6269
+ a kernel-assigned port if <n> is in use.
6270
+
6271
+ hivemind dashboard --help
6272
+ Show this message.
6273
+
6274
+ Data sources (all read-only):
6275
+ - Graph snapshot at ~/.hivemind/graphs/<repo-key>/ (produced by
6276
+ \`hivemind graph build\`; the dashboard works without it and shows
6277
+ an empty-state until the producer has run)
6278
+ - KPIs via the org stats endpoint (cached) with a local fallback
6279
+ to ~/.deeplake/usage-stats.jsonl
6280
+ - Skills created from ~/.claude/skills/<name>--<author>/ directories
6281
+ `;
6282
+ function parsePort(raw) {
6283
+ if (raw === void 0 || raw === "")
6284
+ return { error: "--port requires a value" };
6285
+ const n = Number(raw);
6286
+ if (!Number.isInteger(n) || n < 0 || n > 65535) {
6287
+ return { error: `--port must be an integer in [0, 65535], got '${raw}'` };
6288
+ }
6289
+ return n;
6290
+ }
6291
+ function parseDashboardArgs(args) {
6292
+ let cwd;
6293
+ let outPath = "";
6294
+ let open = true;
6295
+ let serve = false;
6296
+ let port;
6297
+ for (let i = 0; i < args.length; i++) {
6298
+ const a = args[i];
6299
+ if (a === "--help" || a === "-h")
6300
+ return { help: true };
6301
+ if (a === "--no-open") {
6302
+ open = false;
6303
+ continue;
6304
+ }
6305
+ if (a === "--serve") {
6306
+ serve = true;
6307
+ continue;
6308
+ }
6309
+ if (a === "--cwd") {
6310
+ const v = args[++i];
6311
+ if (v === void 0 || v.startsWith("-")) {
6312
+ return { error: "--cwd requires a value" };
6313
+ }
6314
+ cwd = v;
6315
+ continue;
6316
+ }
6317
+ if (a.startsWith("--cwd=")) {
6318
+ cwd = a.slice("--cwd=".length);
6319
+ continue;
6320
+ }
6321
+ if (a === "--out") {
6322
+ const v = args[++i];
6323
+ if (v === void 0 || v.startsWith("-")) {
6324
+ return { error: "--out requires a value" };
6325
+ }
6326
+ outPath = v;
6327
+ continue;
6328
+ }
6329
+ if (a.startsWith("--out=")) {
6330
+ outPath = a.slice("--out=".length);
6331
+ continue;
6332
+ }
6333
+ if (a === "--port") {
6334
+ const v = args[++i];
6335
+ if (v === void 0 || v.startsWith("-")) {
6336
+ return { error: "--port requires a value" };
6337
+ }
6338
+ const parsed = parsePort(v);
6339
+ if (typeof parsed === "object")
6340
+ return { error: parsed.error };
6341
+ port = parsed;
6342
+ continue;
6343
+ }
6344
+ if (a.startsWith("--port=")) {
6345
+ const parsed = parsePort(a.slice("--port=".length));
6346
+ if (typeof parsed === "object")
6347
+ return { error: parsed.error };
6348
+ port = parsed;
6349
+ continue;
6350
+ }
6351
+ return { error: `unknown arg '${a}'` };
6352
+ }
6353
+ if (port !== void 0 && !serve) {
6354
+ return { error: "--port requires --serve" };
6355
+ }
6356
+ return {
6357
+ args: {
6358
+ cwd: cwd ?? process.cwd(),
6359
+ outPath,
6360
+ open,
6361
+ serve,
6362
+ port
6363
+ }
6364
+ };
6365
+ }
6366
+ function defaultDashboardOutPath(repoKey) {
6367
+ return join27(homedir14(), ".hivemind", "dashboards", repoKey, "index.html");
6368
+ }
6369
+ async function runDashboardCommand(rawArgs, runOpts = {}) {
6370
+ const out = runOpts.out ?? ((s) => {
6371
+ process.stdout.write(s);
6372
+ });
6373
+ const err = runOpts.err ?? ((s) => {
6374
+ process.stderr.write(s);
6375
+ });
6376
+ const opener = runOpts.opener ?? openInBrowser;
6377
+ const parsed = parseDashboardArgs(rawArgs);
6378
+ if (parsed.help) {
6379
+ out(USAGE);
6380
+ return 0;
6381
+ }
6382
+ if (parsed.error || !parsed.args) {
6383
+ err(`hivemind dashboard: ${parsed.error ?? "invalid arguments"}
6384
+ `);
6385
+ err(USAGE);
6386
+ return 2;
6387
+ }
6388
+ const { cwd, outPath, open } = parsed.args;
6389
+ let data;
6390
+ try {
6391
+ data = await loadDashboardData({ cwd });
6392
+ } catch (e) {
6393
+ err(`hivemind dashboard: failed to load data: ${e?.message ?? String(e)}
6394
+ `);
6395
+ return 1;
6396
+ }
6397
+ const html = renderDashboardHtml(data);
6398
+ const finalOut = outPath || defaultDashboardOutPath(data.repoKey);
6399
+ const absOut = resolve2(finalOut);
6400
+ try {
6401
+ mkdirSync10(dirname6(absOut), { recursive: true });
6402
+ writeFileSync13(absOut, html, "utf-8");
6403
+ } catch (e) {
6404
+ err(`hivemind dashboard: failed to write ${absOut}: ${e?.message ?? String(e)}
6405
+ `);
6406
+ return 1;
6407
+ }
6408
+ out(`Wrote ${absOut}
6409
+ `);
6410
+ if (data.graph == null) {
6411
+ out(`(no codebase graph yet \u2014 run 'hivemind graph build' to populate)
6412
+ `);
6413
+ }
6414
+ if (parsed.args.serve) {
6415
+ return await runServeLoop(html, parsed.args, runOpts, out, err);
6416
+ }
6417
+ if (open) {
6418
+ const result = opener(absOut);
6419
+ if (result.attempted) {
6420
+ out(`Opening via ${result.command}
6421
+ `);
6422
+ } else {
6423
+ out(`(no opener for this platform; open the file above manually)
6424
+ `);
6425
+ }
6426
+ }
6427
+ return 0;
6428
+ }
6429
+ async function runServeLoop(html, args, runOpts, out, err) {
6430
+ const server = runOpts.server ?? serveDashboardHtml;
6431
+ const opener = runOpts.opener ?? openInBrowser;
6432
+ const onSignal = runOpts.onSignal ?? defaultOnSignal;
6433
+ let handle;
6434
+ try {
6435
+ handle = await server({ html, port: args.port });
6436
+ } catch (e) {
6437
+ err(`hivemind dashboard: failed to start server: ${e?.message ?? String(e)}
6438
+ `);
6439
+ return 1;
6440
+ }
6441
+ const url = `http://${handle.host}:${handle.port}/`;
6442
+ out(`Serving dashboard at ${url} (Ctrl+C to stop)
6443
+ `);
6444
+ if (args.open) {
6445
+ const result = opener(url);
6446
+ if (result.attempted) {
6447
+ out(`Opening via ${result.command}
6448
+ `);
6449
+ } else {
6450
+ out(`(no opener for this platform; click the URL above or open it manually)
6451
+ `);
6452
+ }
6453
+ }
6454
+ let resolveDone;
6455
+ const done = new Promise((r) => {
6456
+ resolveDone = r;
6457
+ });
6458
+ const shutdown = async () => {
6459
+ try {
6460
+ await handle.close();
6461
+ } catch {
6462
+ }
6463
+ resolveDone(0);
6464
+ };
6465
+ const offInt = onSignal("SIGINT", shutdown);
6466
+ const offTerm = onSignal("SIGTERM", shutdown);
6467
+ handle.stopped.then(() => resolveDone(0));
6468
+ try {
6469
+ return await done;
6470
+ } finally {
6471
+ offInt();
6472
+ offTerm();
6473
+ }
6474
+ }
6475
+ function defaultOnSignal(signal, handler) {
6476
+ process.on(signal, handler);
6477
+ return () => process.off(signal, handler);
6478
+ }
6479
+
6480
+ // dist/src/commands/skillify.js
6481
+ import { readdirSync as readdirSync7, existsSync as existsSync30, readFileSync as readFileSync26, mkdirSync as mkdirSync17, renameSync as renameSync8 } from "node:fs";
6482
+ import { homedir as homedir23 } from "node:os";
6483
+ import { dirname as dirname11, join as join38 } from "node:path";
6484
+
5469
6485
  // dist/src/skillify/scope-config.js
6486
+ import { existsSync as existsSync20, mkdirSync as mkdirSync11, readFileSync as readFileSync19, writeFileSync as writeFileSync14 } from "node:fs";
6487
+ import { join as join28 } from "node:path";
5470
6488
  function configPath() {
5471
- return join22(getStateDir(), "config.json");
6489
+ return join28(getStateDir(), "config.json");
5472
6490
  }
5473
6491
  var DEFAULT = { scope: "me", team: [], install: "project" };
5474
6492
  function loadScopeConfig() {
5475
6493
  migrateLegacyStateDir();
5476
6494
  const CONFIG_PATH2 = configPath();
5477
- if (!existsSync16(CONFIG_PATH2))
6495
+ if (!existsSync20(CONFIG_PATH2))
5478
6496
  return DEFAULT;
5479
6497
  try {
5480
- const raw = JSON.parse(readFileSync15(CONFIG_PATH2, "utf-8"));
6498
+ const raw = JSON.parse(readFileSync19(CONFIG_PATH2, "utf-8"));
5481
6499
  const scope = raw.scope === "team" ? "team" : raw.scope === "org" ? "team" : "me";
5482
6500
  const team = Array.isArray(raw.team) ? raw.team.filter((s) => typeof s === "string") : [];
5483
6501
  const install = raw.install === "global" ? "global" : "project";
@@ -5488,19 +6506,19 @@ function loadScopeConfig() {
5488
6506
  }
5489
6507
  function saveScopeConfig(cfg) {
5490
6508
  migrateLegacyStateDir();
5491
- mkdirSync7(getStateDir(), { recursive: true });
5492
- writeFileSync11(configPath(), JSON.stringify(cfg, null, 2));
6509
+ mkdirSync11(getStateDir(), { recursive: true });
6510
+ writeFileSync14(configPath(), JSON.stringify(cfg, null, 2));
5493
6511
  }
5494
6512
 
5495
6513
  // dist/src/skillify/pull.js
5496
- import { existsSync as existsSync20, readFileSync as readFileSync18, writeFileSync as writeFileSync14, mkdirSync as mkdirSync10, renameSync as renameSync6, lstatSync as lstatSync4, readlinkSync as readlinkSync2, symlinkSync as symlinkSync2, unlinkSync as unlinkSync9 } from "node:fs";
5497
- import { homedir as homedir13 } from "node:os";
5498
- import { dirname as dirname5, join as join26 } from "node:path";
6514
+ import { existsSync as existsSync24, readFileSync as readFileSync22, writeFileSync as writeFileSync17, mkdirSync as mkdirSync14, renameSync as renameSync7, lstatSync as lstatSync5, readlinkSync as readlinkSync2, symlinkSync as symlinkSync2, unlinkSync as unlinkSync10 } from "node:fs";
6515
+ import { homedir as homedir17 } from "node:os";
6516
+ import { dirname as dirname8, join as join32 } from "node:path";
5499
6517
 
5500
6518
  // dist/src/skillify/skill-writer.js
5501
- import { existsSync as existsSync17, mkdirSync as mkdirSync8, readFileSync as readFileSync16, readdirSync as readdirSync2, statSync as statSync3, writeFileSync as writeFileSync12 } from "node:fs";
5502
- import { homedir as homedir11 } from "node:os";
5503
- import { join as join23 } from "node:path";
6519
+ import { existsSync as existsSync21, mkdirSync as mkdirSync12, readFileSync as readFileSync20, readdirSync as readdirSync4, statSync as statSync5, writeFileSync as writeFileSync15 } from "node:fs";
6520
+ import { homedir as homedir15 } from "node:os";
6521
+ import { join as join29 } from "node:path";
5504
6522
  function assertValidSkillName(name) {
5505
6523
  if (typeof name !== "string" || name.length === 0) {
5506
6524
  throw new Error(`invalid skill name: empty or non-string`);
@@ -5516,10 +6534,10 @@ function assertValidSkillName(name) {
5516
6534
  }
5517
6535
  }
5518
6536
  function skillDir(skillsRoot, name) {
5519
- return join23(skillsRoot, name);
6537
+ return join29(skillsRoot, name);
5520
6538
  }
5521
6539
  function skillPath(skillsRoot, name) {
5522
- return join23(skillDir(skillsRoot, name), "SKILL.md");
6540
+ return join29(skillDir(skillsRoot, name), "SKILL.md");
5523
6541
  }
5524
6542
  function renderFrontmatter(fm) {
5525
6543
  const lines = ["---"];
@@ -5597,10 +6615,10 @@ function writeNewSkill(args) {
5597
6615
  assertValidSkillName(args.name);
5598
6616
  const dir = skillDir(args.skillsRoot, args.name);
5599
6617
  const path = skillPath(args.skillsRoot, args.name);
5600
- if (existsSync17(path)) {
6618
+ if (existsSync21(path)) {
5601
6619
  throw new Error(`skill already exists at ${path}; use mergeSkill`);
5602
6620
  }
5603
- mkdirSync8(dir, { recursive: true });
6621
+ mkdirSync12(dir, { recursive: true });
5604
6622
  const now = (/* @__PURE__ */ new Date()).toISOString();
5605
6623
  const author = args.author && args.author.length > 0 ? args.author : void 0;
5606
6624
  const contributors = author ? [author] : [];
@@ -5620,7 +6638,7 @@ function writeNewSkill(args) {
5620
6638
 
5621
6639
  ${args.body.trim()}
5622
6640
  `;
5623
- writeFileSync12(path, text);
6641
+ writeFileSync15(path, text);
5624
6642
  return {
5625
6643
  path,
5626
6644
  action: "created",
@@ -5632,40 +6650,40 @@ ${args.body.trim()}
5632
6650
  };
5633
6651
  }
5634
6652
  function listSkills(skillsRoot) {
5635
- if (!existsSync17(skillsRoot))
6653
+ if (!existsSync21(skillsRoot))
5636
6654
  return [];
5637
6655
  const out = [];
5638
- for (const name of readdirSync2(skillsRoot)) {
5639
- const skillFile = join23(skillsRoot, name, "SKILL.md");
5640
- if (existsSync17(skillFile) && statSync3(skillFile).isFile()) {
5641
- out.push({ name, body: readFileSync16(skillFile, "utf-8") });
6656
+ for (const name of readdirSync4(skillsRoot)) {
6657
+ const skillFile = join29(skillsRoot, name, "SKILL.md");
6658
+ if (existsSync21(skillFile) && statSync5(skillFile).isFile()) {
6659
+ out.push({ name, body: readFileSync20(skillFile, "utf-8") });
5642
6660
  }
5643
6661
  }
5644
6662
  return out;
5645
6663
  }
5646
6664
  function resolveSkillsRoot(install, cwd) {
5647
6665
  if (install === "global") {
5648
- return join23(homedir11(), ".claude", "skills");
6666
+ return join29(homedir15(), ".claude", "skills");
5649
6667
  }
5650
- return join23(cwd, ".claude", "skills");
6668
+ return join29(cwd, ".claude", "skills");
5651
6669
  }
5652
6670
 
5653
6671
  // dist/src/skillify/manifest.js
5654
- import { existsSync as existsSync18, lstatSync as lstatSync3, mkdirSync as mkdirSync9, readFileSync as readFileSync17, renameSync as renameSync5, unlinkSync as unlinkSync8, writeFileSync as writeFileSync13 } from "node:fs";
5655
- import { dirname as dirname4, join as join24 } from "node:path";
6672
+ import { existsSync as existsSync22, lstatSync as lstatSync4, mkdirSync as mkdirSync13, readFileSync as readFileSync21, renameSync as renameSync6, unlinkSync as unlinkSync9, writeFileSync as writeFileSync16 } from "node:fs";
6673
+ import { dirname as dirname7, join as join30 } from "node:path";
5656
6674
  function emptyManifest() {
5657
6675
  return { version: 1, entries: [] };
5658
6676
  }
5659
6677
  function manifestPath() {
5660
- return join24(getStateDir(), "pulled.json");
6678
+ return join30(getStateDir(), "pulled.json");
5661
6679
  }
5662
6680
  function loadManifest(path = manifestPath()) {
5663
6681
  migrateLegacyStateDir();
5664
- if (!existsSync18(path))
6682
+ if (!existsSync22(path))
5665
6683
  return emptyManifest();
5666
6684
  let raw;
5667
6685
  try {
5668
- raw = readFileSync17(path, "utf-8");
6686
+ raw = readFileSync21(path, "utf-8");
5669
6687
  } catch {
5670
6688
  return emptyManifest();
5671
6689
  }
@@ -5712,10 +6730,10 @@ function loadManifest(path = manifestPath()) {
5712
6730
  }
5713
6731
  function saveManifest(m, path = manifestPath()) {
5714
6732
  migrateLegacyStateDir();
5715
- mkdirSync9(dirname4(path), { recursive: true });
6733
+ mkdirSync13(dirname7(path), { recursive: true });
5716
6734
  const tmp = `${path}.tmp`;
5717
- writeFileSync13(tmp, JSON.stringify(m, null, 2) + "\n", { mode: 384 });
5718
- renameSync5(tmp, path);
6735
+ writeFileSync16(tmp, JSON.stringify(m, null, 2) + "\n", { mode: 384 });
6736
+ renameSync6(tmp, path);
5719
6737
  }
5720
6738
  function recordPull(entry, path = manifestPath()) {
5721
6739
  const m = loadManifest(path);
@@ -5740,14 +6758,14 @@ function unlinkSymlinks(paths) {
5740
6758
  for (const path of paths) {
5741
6759
  let st;
5742
6760
  try {
5743
- st = lstatSync3(path);
6761
+ st = lstatSync4(path);
5744
6762
  } catch {
5745
6763
  continue;
5746
6764
  }
5747
6765
  if (!st.isSymbolicLink())
5748
6766
  continue;
5749
6767
  try {
5750
- unlinkSync8(path);
6768
+ unlinkSync9(path);
5751
6769
  } catch {
5752
6770
  }
5753
6771
  }
@@ -5757,7 +6775,7 @@ function pruneOrphanedEntries(path = manifestPath()) {
5757
6775
  const live = [];
5758
6776
  let pruned = 0;
5759
6777
  for (const e of m.entries) {
5760
- if (existsSync18(join24(e.installRoot, e.dirName))) {
6778
+ if (existsSync22(join30(e.installRoot, e.dirName))) {
5761
6779
  live.push(e);
5762
6780
  continue;
5763
6781
  }
@@ -5770,26 +6788,26 @@ function pruneOrphanedEntries(path = manifestPath()) {
5770
6788
  }
5771
6789
 
5772
6790
  // dist/src/skillify/agent-roots.js
5773
- import { existsSync as existsSync19 } from "node:fs";
5774
- import { homedir as homedir12 } from "node:os";
5775
- import { join as join25 } from "node:path";
6791
+ import { existsSync as existsSync23 } from "node:fs";
6792
+ import { homedir as homedir16 } from "node:os";
6793
+ import { join as join31 } from "node:path";
5776
6794
  function resolveDetected(home) {
5777
6795
  const out = [];
5778
- const codexInstalled = existsSync19(join25(home, ".codex"));
5779
- const piInstalled = existsSync19(join25(home, ".pi", "agent"));
5780
- const hermesInstalled = existsSync19(join25(home, ".hermes"));
6796
+ const codexInstalled = existsSync23(join31(home, ".codex"));
6797
+ const piInstalled = existsSync23(join31(home, ".pi", "agent"));
6798
+ const hermesInstalled = existsSync23(join31(home, ".hermes"));
5781
6799
  if (codexInstalled || piInstalled) {
5782
- out.push(join25(home, ".agents", "skills"));
6800
+ out.push(join31(home, ".agents", "skills"));
5783
6801
  }
5784
6802
  if (hermesInstalled) {
5785
- out.push(join25(home, ".hermes", "skills"));
6803
+ out.push(join31(home, ".hermes", "skills"));
5786
6804
  }
5787
6805
  if (piInstalled) {
5788
- out.push(join25(home, ".pi", "agent", "skills"));
6806
+ out.push(join31(home, ".pi", "agent", "skills"));
5789
6807
  }
5790
6808
  return out;
5791
6809
  }
5792
- function detectAgentSkillsRoots(canonicalRoot, home = homedir12()) {
6810
+ function detectAgentSkillsRoots(canonicalRoot, home = homedir16()) {
5793
6811
  return resolveDetected(home).filter((p) => p !== canonicalRoot);
5794
6812
  }
5795
6813
 
@@ -5833,18 +6851,18 @@ function isMissingTableError(message) {
5833
6851
  }
5834
6852
  function resolvePullDestination(install, cwd) {
5835
6853
  if (install === "global")
5836
- return join26(homedir13(), ".claude", "skills");
6854
+ return join32(homedir17(), ".claude", "skills");
5837
6855
  if (!cwd)
5838
6856
  throw new Error("install=project requires a cwd");
5839
- return join26(cwd, ".claude", "skills");
6857
+ return join32(cwd, ".claude", "skills");
5840
6858
  }
5841
6859
  function fanOutSymlinks(canonicalDir, dirName, agentRoots) {
5842
6860
  const out = [];
5843
6861
  for (const root of agentRoots) {
5844
- const link = join26(root, dirName);
6862
+ const link = join32(root, dirName);
5845
6863
  let existing;
5846
6864
  try {
5847
- existing = lstatSync4(link);
6865
+ existing = lstatSync5(link);
5848
6866
  } catch {
5849
6867
  existing = null;
5850
6868
  }
@@ -5863,13 +6881,13 @@ function fanOutSymlinks(canonicalDir, dirName, agentRoots) {
5863
6881
  continue;
5864
6882
  }
5865
6883
  try {
5866
- unlinkSync9(link);
6884
+ unlinkSync10(link);
5867
6885
  } catch {
5868
6886
  continue;
5869
6887
  }
5870
6888
  }
5871
6889
  try {
5872
- mkdirSync10(dirname5(link), { recursive: true });
6890
+ mkdirSync14(dirname8(link), { recursive: true });
5873
6891
  symlinkSync2(canonicalDir, link, "dir");
5874
6892
  out.push(link);
5875
6893
  } catch {
@@ -5884,8 +6902,8 @@ function backfillSymlinks(installRoot) {
5884
6902
  return;
5885
6903
  const detected = detectAgentSkillsRoots(installRoot);
5886
6904
  for (const entry of entries) {
5887
- const canonical = join26(entry.installRoot, entry.dirName);
5888
- if (!existsSync20(canonical))
6905
+ const canonical = join32(entry.installRoot, entry.dirName);
6906
+ if (!existsSync24(canonical))
5889
6907
  continue;
5890
6908
  const fresh = fanOutSymlinks(canonical, entry.dirName, detected);
5891
6909
  if (sameSorted(fresh, entry.symlinks))
@@ -5995,10 +7013,10 @@ function renderFrontmatter2(fm) {
5995
7013
  return lines.join("\n");
5996
7014
  }
5997
7015
  function readLocalVersion(path) {
5998
- if (!existsSync20(path))
7016
+ if (!existsSync24(path))
5999
7017
  return null;
6000
7018
  try {
6001
- const text = readFileSync18(path, "utf-8");
7019
+ const text = readFileSync22(path, "utf-8");
6002
7020
  const parsed = parseFrontmatter(text);
6003
7021
  if (!parsed)
6004
7022
  return null;
@@ -6093,8 +7111,8 @@ async function runPull(opts) {
6093
7111
  summary.skipped++;
6094
7112
  continue;
6095
7113
  }
6096
- const skillDir2 = join26(root, dirName);
6097
- const skillFile = join26(skillDir2, "SKILL.md");
7114
+ const skillDir2 = join32(root, dirName);
7115
+ const skillFile = join32(skillDir2, "SKILL.md");
6098
7116
  const remoteVersion = Number(row.version ?? 1);
6099
7117
  const localVersion = readLocalVersion(skillFile);
6100
7118
  const action = decideAction({
@@ -6105,14 +7123,14 @@ async function runPull(opts) {
6105
7123
  });
6106
7124
  let manifestError;
6107
7125
  if (action === "wrote") {
6108
- mkdirSync10(skillDir2, { recursive: true });
6109
- if (existsSync20(skillFile)) {
7126
+ mkdirSync14(skillDir2, { recursive: true });
7127
+ if (existsSync24(skillFile)) {
6110
7128
  try {
6111
- renameSync6(skillFile, `${skillFile}.bak`);
7129
+ renameSync7(skillFile, `${skillFile}.bak`);
6112
7130
  } catch {
6113
7131
  }
6114
7132
  }
6115
- writeFileSync14(skillFile, renderSkillFile(row));
7133
+ writeFileSync17(skillFile, renderSkillFile(row));
6116
7134
  const symlinks = opts.install === "global" ? fanOutSymlinks(skillDir2, dirName, detectAgentSkillsRoots(root)) : [];
6117
7135
  try {
6118
7136
  recordPull({
@@ -6154,15 +7172,15 @@ async function runPull(opts) {
6154
7172
  }
6155
7173
 
6156
7174
  // dist/src/skillify/unpull.js
6157
- import { existsSync as existsSync21, readdirSync as readdirSync3, rmSync as rmSync5, statSync as statSync4 } from "node:fs";
6158
- import { homedir as homedir14 } from "node:os";
6159
- import { join as join27 } from "node:path";
7175
+ import { existsSync as existsSync25, readdirSync as readdirSync5, rmSync as rmSync5, statSync as statSync6 } from "node:fs";
7176
+ import { homedir as homedir18 } from "node:os";
7177
+ import { join as join33 } from "node:path";
6160
7178
  function resolveUnpullRoot(install, cwd) {
6161
7179
  if (install === "global")
6162
- return join27(homedir14(), ".claude", "skills");
7180
+ return join33(homedir18(), ".claude", "skills");
6163
7181
  if (!cwd)
6164
7182
  throw new Error("cwd required when install === 'project'");
6165
- return join27(cwd, ".claude", "skills");
7183
+ return join33(cwd, ".claude", "skills");
6166
7184
  }
6167
7185
  function runUnpull(opts) {
6168
7186
  const root = resolveUnpullRoot(opts.install, opts.cwd);
@@ -6185,8 +7203,8 @@ function runUnpull(opts) {
6185
7203
  const entries = entriesForRoot(manifest, opts.install, root);
6186
7204
  for (const entry of entries) {
6187
7205
  summary.scanned++;
6188
- const path = join27(root, entry.dirName);
6189
- if (!existsSync21(path)) {
7206
+ const path = join33(root, entry.dirName);
7207
+ if (!existsSync25(path)) {
6190
7208
  if (!opts.dryRun) {
6191
7209
  unlinkSymlinks(entry.symlinks);
6192
7210
  removePullEntry(opts.install, entry.installRoot, entry.dirName);
@@ -6239,15 +7257,15 @@ function runUnpull(opts) {
6239
7257
  }
6240
7258
  summary.entries.push(result);
6241
7259
  }
6242
- if (existsSync21(root) && (opts.all || opts.legacyCleanup)) {
7260
+ if (existsSync25(root) && (opts.all || opts.legacyCleanup)) {
6243
7261
  const manifestDirNames = new Set(entries.map((e) => e.dirName));
6244
- for (const dirName of readdirSync3(root)) {
7262
+ for (const dirName of readdirSync5(root)) {
6245
7263
  if (manifestDirNames.has(dirName))
6246
7264
  continue;
6247
- const path = join27(root, dirName);
7265
+ const path = join33(root, dirName);
6248
7266
  let st;
6249
7267
  try {
6250
- st = statSync4(path);
7268
+ st = statSync6(path);
6251
7269
  } catch {
6252
7270
  continue;
6253
7271
  }
@@ -6322,31 +7340,31 @@ function decideTargetForManifestEntry(entry, opts, userFilter, haveUserFilter) {
6322
7340
  }
6323
7341
 
6324
7342
  // dist/src/commands/mine-local.js
6325
- import { spawn } from "node:child_process";
6326
- import { existsSync as existsSync25, mkdirSync as mkdirSync12, readFileSync as readFileSync21, writeFileSync as writeFileSync16 } from "node:fs";
6327
- import { homedir as homedir18 } from "node:os";
6328
- import { basename, dirname as dirname7, join as join31 } from "node:path";
7343
+ import { spawn as spawn2 } from "node:child_process";
7344
+ import { existsSync as existsSync29, mkdirSync as mkdirSync16, readFileSync as readFileSync25, writeFileSync as writeFileSync19 } from "node:fs";
7345
+ import { homedir as homedir22 } from "node:os";
7346
+ import { basename as basename2, dirname as dirname10, join as join37 } from "node:path";
6329
7347
 
6330
7348
  // dist/src/skillify/local-source.js
6331
- import { readdirSync as readdirSync4, readFileSync as readFileSync19, existsSync as existsSync22, statSync as statSync5 } from "node:fs";
6332
- import { homedir as homedir15 } from "node:os";
6333
- import { join as join28 } from "node:path";
6334
- var HOME2 = homedir15();
7349
+ import { readdirSync as readdirSync6, readFileSync as readFileSync23, existsSync as existsSync26, statSync as statSync7 } from "node:fs";
7350
+ import { homedir as homedir19 } from "node:os";
7351
+ import { join as join34 } from "node:path";
7352
+ var HOME2 = homedir19();
6335
7353
  function encodeCwdClaudeCode(cwd) {
6336
7354
  return cwd.replace(/[/_]/g, "-");
6337
7355
  }
6338
7356
  function detectInstalledAgents() {
6339
7357
  const installs = [];
6340
- const claudeRoot = join28(HOME2, ".claude", "projects");
6341
- if (existsSync22(claudeRoot)) {
7358
+ const claudeRoot = join34(HOME2, ".claude", "projects");
7359
+ if (existsSync26(claudeRoot)) {
6342
7360
  installs.push({
6343
7361
  agent: "claude_code",
6344
7362
  sessionRoot: claudeRoot,
6345
7363
  encodeCwd: encodeCwdClaudeCode
6346
7364
  });
6347
7365
  }
6348
- const codexRoot = join28(HOME2, ".codex", "sessions");
6349
- if (existsSync22(codexRoot)) {
7366
+ const codexRoot = join34(HOME2, ".codex", "sessions");
7367
+ if (existsSync26(codexRoot)) {
6350
7368
  installs.push({
6351
7369
  agent: "codex",
6352
7370
  sessionRoot: codexRoot,
@@ -6368,14 +7386,14 @@ function listLocalSessions(installs, cwd) {
6368
7386
  const cwdEncoded = install.encodeCwd(cwd);
6369
7387
  let subdirs = [];
6370
7388
  try {
6371
- subdirs = readdirSync4(install.sessionRoot);
7389
+ subdirs = readdirSync6(install.sessionRoot);
6372
7390
  } catch {
6373
7391
  continue;
6374
7392
  }
6375
7393
  for (const sub of subdirs) {
6376
- const subdirPath = join28(install.sessionRoot, sub);
7394
+ const subdirPath = join34(install.sessionRoot, sub);
6377
7395
  try {
6378
- if (!statSync5(subdirPath).isDirectory())
7396
+ if (!statSync7(subdirPath).isDirectory())
6379
7397
  continue;
6380
7398
  } catch {
6381
7399
  continue;
@@ -6383,17 +7401,17 @@ function listLocalSessions(installs, cwd) {
6383
7401
  const inCwd = sub === cwdEncoded;
6384
7402
  let files = [];
6385
7403
  try {
6386
- files = readdirSync4(subdirPath);
7404
+ files = readdirSync6(subdirPath);
6387
7405
  } catch {
6388
7406
  continue;
6389
7407
  }
6390
7408
  for (const f of files) {
6391
7409
  if (!f.endsWith(".jsonl"))
6392
7410
  continue;
6393
- const fullPath = join28(subdirPath, f);
7411
+ const fullPath = join34(subdirPath, f);
6394
7412
  let stats;
6395
7413
  try {
6396
- stats = statSync5(fullPath);
7414
+ stats = statSync7(fullPath);
6397
7415
  } catch {
6398
7416
  continue;
6399
7417
  }
@@ -6451,7 +7469,7 @@ function pickSessions(candidates, opts) {
6451
7469
  function nativeJsonlToRows(filePath, sessionId, agent) {
6452
7470
  let raw;
6453
7471
  try {
6454
- raw = readFileSync19(filePath, "utf-8");
7472
+ raw = readFileSync23(filePath, "utf-8");
6455
7473
  } catch {
6456
7474
  return [];
6457
7475
  }
@@ -6541,22 +7559,22 @@ function extractPairs(rows) {
6541
7559
  }
6542
7560
 
6543
7561
  // dist/src/skillify/gate-runner.js
6544
- import { existsSync as existsSync23 } from "node:fs";
7562
+ import { existsSync as existsSync27 } from "node:fs";
6545
7563
  import { createRequire } from "node:module";
6546
- import { homedir as homedir16 } from "node:os";
6547
- import { join as join29 } from "node:path";
7564
+ import { homedir as homedir20 } from "node:os";
7565
+ import { join as join35 } from "node:path";
6548
7566
  var requireForCp = createRequire(import.meta.url);
6549
7567
  var { execFileSync: runChildProcess } = requireForCp("node:child_process");
6550
7568
  var inheritedEnv = process;
6551
7569
  function firstExistingPath(candidates) {
6552
7570
  for (const c of candidates) {
6553
- if (existsSync23(c))
7571
+ if (existsSync27(c))
6554
7572
  return c;
6555
7573
  }
6556
7574
  return null;
6557
7575
  }
6558
7576
  function findAgentBin(agent) {
6559
- const home = homedir16();
7577
+ const home = homedir20();
6560
7578
  switch (agent) {
6561
7579
  // /usr/bin/<name> is included in every candidate list — that's the
6562
7580
  // common Linux package-manager install path (apt, dnf, pacman). Old
@@ -6565,45 +7583,45 @@ function findAgentBin(agent) {
6565
7583
  // #170 caught the gap.
6566
7584
  case "claude_code":
6567
7585
  return firstExistingPath([
6568
- join29(home, ".claude", "local", "claude"),
7586
+ join35(home, ".claude", "local", "claude"),
6569
7587
  "/usr/local/bin/claude",
6570
7588
  "/usr/bin/claude",
6571
- join29(home, ".npm-global", "bin", "claude"),
6572
- join29(home, ".local", "bin", "claude"),
7589
+ join35(home, ".npm-global", "bin", "claude"),
7590
+ join35(home, ".local", "bin", "claude"),
6573
7591
  "/opt/homebrew/bin/claude"
6574
- ]) ?? join29(home, ".claude", "local", "claude");
7592
+ ]) ?? join35(home, ".claude", "local", "claude");
6575
7593
  case "codex":
6576
7594
  return firstExistingPath([
6577
7595
  "/usr/local/bin/codex",
6578
7596
  "/usr/bin/codex",
6579
- join29(home, ".npm-global", "bin", "codex"),
6580
- join29(home, ".local", "bin", "codex"),
7597
+ join35(home, ".npm-global", "bin", "codex"),
7598
+ join35(home, ".local", "bin", "codex"),
6581
7599
  "/opt/homebrew/bin/codex"
6582
7600
  ]) ?? "/usr/local/bin/codex";
6583
7601
  case "cursor":
6584
7602
  return firstExistingPath([
6585
7603
  "/usr/local/bin/cursor-agent",
6586
7604
  "/usr/bin/cursor-agent",
6587
- join29(home, ".npm-global", "bin", "cursor-agent"),
6588
- join29(home, ".local", "bin", "cursor-agent"),
7605
+ join35(home, ".npm-global", "bin", "cursor-agent"),
7606
+ join35(home, ".local", "bin", "cursor-agent"),
6589
7607
  "/opt/homebrew/bin/cursor-agent"
6590
7608
  ]) ?? "/usr/local/bin/cursor-agent";
6591
7609
  case "hermes":
6592
7610
  return firstExistingPath([
6593
- join29(home, ".local", "bin", "hermes"),
7611
+ join35(home, ".local", "bin", "hermes"),
6594
7612
  "/usr/local/bin/hermes",
6595
7613
  "/usr/bin/hermes",
6596
- join29(home, ".npm-global", "bin", "hermes"),
7614
+ join35(home, ".npm-global", "bin", "hermes"),
6597
7615
  "/opt/homebrew/bin/hermes"
6598
- ]) ?? join29(home, ".local", "bin", "hermes");
7616
+ ]) ?? join35(home, ".local", "bin", "hermes");
6599
7617
  case "pi":
6600
7618
  return firstExistingPath([
6601
- join29(home, ".local", "bin", "pi"),
7619
+ join35(home, ".local", "bin", "pi"),
6602
7620
  "/usr/local/bin/pi",
6603
7621
  "/usr/bin/pi",
6604
- join29(home, ".npm-global", "bin", "pi"),
7622
+ join35(home, ".npm-global", "bin", "pi"),
6605
7623
  "/opt/homebrew/bin/pi"
6606
- ]) ?? join29(home, ".local", "bin", "pi");
7624
+ ]) ?? join35(home, ".local", "bin", "pi");
6607
7625
  }
6608
7626
  }
6609
7627
 
@@ -6633,28 +7651,28 @@ function extractJsonBlock(s) {
6633
7651
  }
6634
7652
 
6635
7653
  // dist/src/skillify/local-manifest.js
6636
- import { existsSync as existsSync24, mkdirSync as mkdirSync11, readFileSync as readFileSync20, writeFileSync as writeFileSync15 } from "node:fs";
6637
- import { homedir as homedir17 } from "node:os";
6638
- import { dirname as dirname6, join as join30 } from "node:path";
6639
- var LOCAL_MANIFEST_PATH = join30(homedir17(), ".claude", "hivemind", "local-mined.json");
6640
- var LOCAL_MINE_LOCK_PATH = join30(homedir17(), ".claude", "hivemind", "local-mined.lock");
7654
+ import { existsSync as existsSync28, mkdirSync as mkdirSync15, readFileSync as readFileSync24, writeFileSync as writeFileSync18 } from "node:fs";
7655
+ import { homedir as homedir21 } from "node:os";
7656
+ import { dirname as dirname9, join as join36 } from "node:path";
7657
+ var LOCAL_MANIFEST_PATH = join36(homedir21(), ".claude", "hivemind", "local-mined.json");
7658
+ var LOCAL_MINE_LOCK_PATH = join36(homedir21(), ".claude", "hivemind", "local-mined.lock");
6641
7659
  function readLocalManifest(path = LOCAL_MANIFEST_PATH) {
6642
- if (!existsSync24(path))
7660
+ if (!existsSync28(path))
6643
7661
  return null;
6644
7662
  try {
6645
- return JSON.parse(readFileSync20(path, "utf-8"));
7663
+ return JSON.parse(readFileSync24(path, "utf-8"));
6646
7664
  } catch {
6647
7665
  return null;
6648
7666
  }
6649
7667
  }
6650
7668
  function writeLocalManifest(m, path = LOCAL_MANIFEST_PATH) {
6651
- mkdirSync11(dirname6(path), { recursive: true });
6652
- writeFileSync15(path, JSON.stringify(m, null, 2));
7669
+ mkdirSync15(dirname9(path), { recursive: true });
7670
+ writeFileSync18(path, JSON.stringify(m, null, 2));
6653
7671
  }
6654
7672
  var LATEST_RUN_WINDOW_MS = 5 * 60 * 1e3;
6655
7673
 
6656
7674
  // dist/src/commands/mine-local.js
6657
- import { unlinkSync as unlinkSync10 } from "node:fs";
7675
+ import { unlinkSync as unlinkSync11 } from "node:fs";
6658
7676
  var EPSILON = 0.3;
6659
7677
  var DEFAULT_N = 8;
6660
7678
  var PAIR_CHAR_CAP = 4e3;
@@ -6665,9 +7683,9 @@ var IN_FLIGHT_MAX_AGE_MS = 6e4;
6665
7683
  var GATE_TIMEOUT_MS = 24e4;
6666
7684
  var MANIFEST_PATH = LOCAL_MANIFEST_PATH;
6667
7685
  function runGateViaStdin(opts) {
6668
- return new Promise((resolve2) => {
7686
+ return new Promise((resolve3) => {
6669
7687
  if (opts.agent !== "claude_code") {
6670
- resolve2({
7688
+ resolve3({
6671
7689
  stdout: "",
6672
7690
  stderr: "",
6673
7691
  errored: true,
@@ -6675,8 +7693,8 @@ function runGateViaStdin(opts) {
6675
7693
  });
6676
7694
  return;
6677
7695
  }
6678
- if (!existsSync25(opts.bin)) {
6679
- resolve2({
7696
+ if (!existsSync29(opts.bin)) {
7697
+ resolve3({
6680
7698
  stdout: "",
6681
7699
  stderr: "",
6682
7700
  errored: true,
@@ -6692,7 +7710,7 @@ function runGateViaStdin(opts) {
6692
7710
  "--permission-mode",
6693
7711
  "bypassPermissions"
6694
7712
  ];
6695
- const child = spawn(opts.bin, args, {
7713
+ const child = spawn2(opts.bin, args, {
6696
7714
  stdio: ["pipe", "pipe", "pipe"],
6697
7715
  env: { ...process.env, HIVEMIND_WIKI_WORKER: "1", HIVEMIND_CAPTURE: "false" }
6698
7716
  });
@@ -6703,7 +7721,7 @@ function runGateViaStdin(opts) {
6703
7721
  if (settled)
6704
7722
  return;
6705
7723
  settled = true;
6706
- resolve2(r);
7724
+ resolve3(r);
6707
7725
  };
6708
7726
  const timer = setTimeout(() => {
6709
7727
  try {
@@ -6982,7 +8000,7 @@ async function runMineLocal(args) {
6982
8000
  return;
6983
8001
  lockReleased = true;
6984
8002
  try {
6985
- unlinkSync10(LOCAL_MINE_LOCK_PATH);
8003
+ unlinkSync11(LOCAL_MINE_LOCK_PATH);
6986
8004
  } catch {
6987
8005
  }
6988
8006
  };
@@ -7039,8 +8057,8 @@ async function runMineLocalImpl(args) {
7039
8057
  console.log(`Dry-run: would invoke ${gateAgent} gate on ${picked.length} session(s) in parallel (concurrency=${GATE_CONCURRENCY}).`);
7040
8058
  return;
7041
8059
  }
7042
- const tmpDir = join31(homedir18(), ".claude", "hivemind", `mine-local-${Date.now()}`);
7043
- mkdirSync12(tmpDir, { recursive: true });
8060
+ const tmpDir = join37(homedir22(), ".claude", "hivemind", `mine-local-${Date.now()}`);
8061
+ mkdirSync16(tmpDir, { recursive: true });
7044
8062
  console.log(`Running ${picked.length} gate call(s) in parallel (concurrency=${GATE_CONCURRENCY}, timeout=${GATE_TIMEOUT_MS / 1e3}s each)...`);
7045
8063
  const results = await parallelMap(picked, GATE_CONCURRENCY, async (s) => {
7046
8064
  const shortId = s.sessionId.slice(0, 8);
@@ -7051,23 +8069,23 @@ async function runMineLocalImpl(args) {
7051
8069
  return { session: s, skills: [], reason: "no pairs", error: null };
7052
8070
  }
7053
8071
  const tail = pairs2.slice(-PER_SESSION_PAIR_CAP);
7054
- const sessionTmp = join31(tmpDir, `s-${shortId}`);
7055
- mkdirSync12(sessionTmp, { recursive: true });
7056
- const verdictPath = join31(sessionTmp, "verdict.json");
8072
+ const sessionTmp = join37(tmpDir, `s-${shortId}`);
8073
+ mkdirSync16(sessionTmp, { recursive: true });
8074
+ const verdictPath = join37(sessionTmp, "verdict.json");
7057
8075
  const prompt = buildSessionPrompt(tail, s, verdictPath);
7058
- writeFileSync16(join31(sessionTmp, "prompt.txt"), prompt);
8076
+ writeFileSync19(join37(sessionTmp, "prompt.txt"), prompt);
7059
8077
  const gate = await runGateViaStdin({ agent: gateAgent, bin: gateBin, prompt, timeoutMs: GATE_TIMEOUT_MS });
7060
8078
  try {
7061
- writeFileSync16(join31(sessionTmp, "gate-stdout.txt"), gate.stdout);
8079
+ writeFileSync19(join37(sessionTmp, "gate-stdout.txt"), gate.stdout);
7062
8080
  if (gate.stderr)
7063
- writeFileSync16(join31(sessionTmp, "gate-stderr.txt"), gate.stderr);
8081
+ writeFileSync19(join37(sessionTmp, "gate-stderr.txt"), gate.stderr);
7064
8082
  } catch {
7065
8083
  }
7066
8084
  if (gate.errored) {
7067
8085
  console.log(` [${shortId}] gate failed: ${gate.errorMessage}`);
7068
8086
  return { session: s, skills: [], reason: null, error: gate.errorMessage ?? "gate failed" };
7069
8087
  }
7070
- const verdictText = existsSync25(verdictPath) ? readFileSync21(verdictPath, "utf-8") : gate.stdout;
8088
+ const verdictText = existsSync29(verdictPath) ? readFileSync25(verdictPath, "utf-8") : gate.stdout;
7071
8089
  const mv = parseMultiVerdict(verdictText);
7072
8090
  if (!mv) {
7073
8091
  console.log(` [${shortId}] unparseable verdict (kept at ${sessionTmp})`);
@@ -7119,8 +8137,8 @@ async function runMineLocalImpl(args) {
7119
8137
  sourceSessions: [session.sessionId],
7120
8138
  agent: gateAgent
7121
8139
  });
7122
- const canonicalDir = dirname7(result.path);
7123
- const symlinks = fanOutRoots.length > 0 ? fanOutSymlinks(canonicalDir, basename(canonicalDir), fanOutRoots) : [];
8140
+ const canonicalDir = dirname10(result.path);
8141
+ const symlinks = fanOutRoots.length > 0 ? fanOutSymlinks(canonicalDir, basename2(canonicalDir), fanOutRoots) : [];
7124
8142
  const symlinkSuffix = symlinks.length > 0 ? `, fan-out \u2192 ${symlinks.length} root(s)` : "";
7125
8143
  console.log(` wrote ${skill.name} \u2190 session ${session.sessionId.slice(0, 8)} (${session.agent}${symlinkSuffix})`);
7126
8144
  written.push({ skill, session, result, symlinks });
@@ -7296,11 +8314,11 @@ function showStatus() {
7296
8314
  console.log(`team: ${cfg.team.length === 0 ? "(empty)" : cfg.team.join(", ")}`);
7297
8315
  console.log(`install: ${cfg.install} (${cfg.install === "global" ? "~/.claude/skills/" : "<project>/.claude/skills/"})`);
7298
8316
  const dir = stateDir();
7299
- if (!existsSync26(dir)) {
8317
+ if (!existsSync30(dir)) {
7300
8318
  console.log(`state: (no projects tracked yet)`);
7301
8319
  return;
7302
8320
  }
7303
- const files = readdirSync5(dir).filter((f) => f.endsWith(".json") && f !== "config.json" && f !== "pulled.json" && f !== "autopull-last-run.json");
8321
+ const files = readdirSync7(dir).filter((f) => f.endsWith(".json") && f !== "config.json" && f !== "pulled.json" && f !== "autopull-last-run.json");
7304
8322
  if (files.length === 0) {
7305
8323
  console.log(`state: (no projects tracked yet)`);
7306
8324
  return;
@@ -7308,7 +8326,7 @@ function showStatus() {
7308
8326
  console.log(`state: ${files.length} project(s) tracked`);
7309
8327
  for (const f of files) {
7310
8328
  try {
7311
- const s = JSON.parse(readFileSync22(join32(dir, f), "utf-8"));
8329
+ const s = JSON.parse(readFileSync26(join38(dir, f), "utf-8"));
7312
8330
  const last = typeof s.updatedAt === "number" ? new Date(s.updatedAt).toISOString() : s.lastDate ?? "never";
7313
8331
  const skills = Array.isArray(s.skillsGenerated) && s.skillsGenerated.length > 0 ? s.skillsGenerated.join(", ") : "none";
7314
8332
  console.log(` - ${s.project} (counter=${s.counter}, last=${last}, skills=${skills})`);
@@ -7335,7 +8353,7 @@ function setInstall(loc) {
7335
8353
  }
7336
8354
  const cfg = loadScopeConfig();
7337
8355
  saveScopeConfig({ ...cfg, install: loc });
7338
- const path = loc === "global" ? join32(homedir19(), ".claude", "skills") : "<cwd>/.claude/skills";
8356
+ const path = loc === "global" ? join38(homedir23(), ".claude", "skills") : "<cwd>/.claude/skills";
7339
8357
  console.log(`Install location set to '${loc}'. New skills will be written to ${path}/<name>/SKILL.md.`);
7340
8358
  }
7341
8359
  function promoteSkill(name, cwd) {
@@ -7343,18 +8361,18 @@ function promoteSkill(name, cwd) {
7343
8361
  console.error("Usage: hivemind skillify promote <skill-name>");
7344
8362
  process.exit(1);
7345
8363
  }
7346
- const projectPath = join32(cwd, ".claude", "skills", name);
7347
- const globalPath = join32(homedir19(), ".claude", "skills", name);
7348
- if (!existsSync26(join32(projectPath, "SKILL.md"))) {
8364
+ const projectPath = join38(cwd, ".claude", "skills", name);
8365
+ const globalPath = join38(homedir23(), ".claude", "skills", name);
8366
+ if (!existsSync30(join38(projectPath, "SKILL.md"))) {
7349
8367
  console.error(`Skill '${name}' not found at ${projectPath}/SKILL.md`);
7350
8368
  process.exit(1);
7351
8369
  }
7352
- if (existsSync26(join32(globalPath, "SKILL.md"))) {
8370
+ if (existsSync30(join38(globalPath, "SKILL.md"))) {
7353
8371
  console.error(`Skill '${name}' already exists at ${globalPath}/SKILL.md \u2014 refusing to overwrite. Remove it first or rename the project skill.`);
7354
8372
  process.exit(1);
7355
8373
  }
7356
- mkdirSync13(dirname8(globalPath), { recursive: true });
7357
- renameSync7(projectPath, globalPath);
8374
+ mkdirSync17(dirname11(globalPath), { recursive: true });
8375
+ renameSync8(projectPath, globalPath);
7358
8376
  console.log(`Promoted '${name}' from ${projectPath} \u2192 ${globalPath}.`);
7359
8377
  }
7360
8378
  function teamAdd(name) {
@@ -7460,7 +8478,7 @@ async function pullSkills(args) {
7460
8478
  console.error(`pull failed: ${e?.message ?? e}`);
7461
8479
  process.exit(1);
7462
8480
  }
7463
- const dest = toRaw === "global" ? join32(homedir19(), ".claude", "skills") : `${process.cwd()}/.claude/skills`;
8481
+ const dest = toRaw === "global" ? join38(homedir23(), ".claude", "skills") : `${process.cwd()}/.claude/skills`;
7464
8482
  const filterDesc = users.length === 0 ? "all users" : users.join(", ");
7465
8483
  console.log(`Destination: ${dest}`);
7466
8484
  console.log(`Filter: ${filterDesc}${skillName ? ` \xB7 skill='${skillName}'` : ""}${dryRun ? " \xB7 dry-run" : ""}${force ? " \xB7 force" : ""}`);
@@ -7510,7 +8528,7 @@ async function unpullSkills(args) {
7510
8528
  all,
7511
8529
  legacyCleanup
7512
8530
  });
7513
- const dest = toRaw === "global" ? join32(homedir19(), ".claude", "skills") : `${process.cwd()}/.claude/skills`;
8531
+ const dest = toRaw === "global" ? join38(homedir23(), ".claude", "skills") : `${process.cwd()}/.claude/skills`;
7514
8532
  const filterParts = [];
7515
8533
  if (users.length > 0)
7516
8534
  filterParts.push(`users=${users.join(",")}`);
@@ -7606,14 +8624,14 @@ if (process.argv[1] && process.argv[1].endsWith("skillify.js")) {
7606
8624
 
7607
8625
  // dist/src/cli/update.js
7608
8626
  import { execFileSync as execFileSync4 } from "node:child_process";
7609
- import { closeSync as closeSync2, existsSync as existsSync27, mkdirSync as mkdirSync14, openSync as openSync2, readFileSync as readFileSync24, realpathSync, unlinkSync as unlinkSync11, writeSync } from "node:fs";
7610
- import { homedir as homedir20 } from "node:os";
7611
- import { dirname as dirname10, join as join34, sep } from "node:path";
8627
+ import { closeSync as closeSync3, existsSync as existsSync31, mkdirSync as mkdirSync18, openSync as openSync3, readFileSync as readFileSync28, realpathSync, unlinkSync as unlinkSync12, writeSync as writeSync2 } from "node:fs";
8628
+ import { homedir as homedir24 } from "node:os";
8629
+ import { dirname as dirname13, join as join40, sep } from "node:path";
7612
8630
  import { fileURLToPath as fileURLToPath2 } from "node:url";
7613
8631
 
7614
8632
  // dist/src/utils/version-check.js
7615
- import { readFileSync as readFileSync23 } from "node:fs";
7616
- import { dirname as dirname9, join as join33 } from "node:path";
8633
+ import { readFileSync as readFileSync27 } from "node:fs";
8634
+ import { dirname as dirname12, join as join39 } from "node:path";
7617
8635
  function isNewer(latest, current) {
7618
8636
  const parse = (v) => v.split(".").map(Number);
7619
8637
  const [la, lb, lc] = parse(latest);
@@ -7625,7 +8643,7 @@ function isNewer(latest, current) {
7625
8643
  var NPM_REGISTRY_URL = "https://registry.npmjs.org/@deeplake/hivemind/latest";
7626
8644
  var PKG_NAME = "@deeplake/hivemind";
7627
8645
  function defaultLockPath() {
7628
- return join34(homedir20(), ".deeplake", "hivemind-update.lock");
8646
+ return join40(homedir24(), ".deeplake", "hivemind-update.lock");
7629
8647
  }
7630
8648
  function detectInstallKind(argv1) {
7631
8649
  const realArgv1 = (() => {
@@ -7635,24 +8653,24 @@ function detectInstallKind(argv1) {
7635
8653
  return argv1 ?? process.argv[1] ?? fileURLToPath2(import.meta.url);
7636
8654
  }
7637
8655
  })();
7638
- let dir = dirname10(realArgv1);
8656
+ let dir = dirname13(realArgv1);
7639
8657
  let installDir = null;
7640
8658
  for (let i = 0; i < 10; i++) {
7641
8659
  const pkgPath = `${dir}${sep}package.json`;
7642
8660
  try {
7643
- const pkg = JSON.parse(readFileSync24(pkgPath, "utf-8"));
8661
+ const pkg = JSON.parse(readFileSync28(pkgPath, "utf-8"));
7644
8662
  if (pkg.name === PKG_NAME || pkg.name === "hivemind") {
7645
8663
  installDir = dir;
7646
8664
  break;
7647
8665
  }
7648
8666
  } catch {
7649
8667
  }
7650
- const parent = dirname10(dir);
8668
+ const parent = dirname13(dir);
7651
8669
  if (parent === dir)
7652
8670
  break;
7653
8671
  dir = parent;
7654
8672
  }
7655
- installDir ??= dirname10(realArgv1);
8673
+ installDir ??= dirname13(realArgv1);
7656
8674
  if (realArgv1.includes(`${sep}_npx${sep}`) || realArgv1.includes(`${sep}.npx${sep}`)) {
7657
8675
  return { kind: "npx", installDir };
7658
8676
  }
@@ -7661,10 +8679,10 @@ function detectInstallKind(argv1) {
7661
8679
  }
7662
8680
  let gitDir = installDir;
7663
8681
  for (let i = 0; i < 6; i++) {
7664
- if (existsSync27(`${gitDir}${sep}.git`)) {
8682
+ if (existsSync31(`${gitDir}${sep}.git`)) {
7665
8683
  return { kind: "local-dev", installDir };
7666
8684
  }
7667
- const parent = dirname10(gitDir);
8685
+ const parent = dirname13(gitDir);
7668
8686
  if (parent === gitDir)
7669
8687
  break;
7670
8688
  gitDir = parent;
@@ -7686,10 +8704,10 @@ var defaultSpawn = (cmd, args) => {
7686
8704
  execFileSync4(cmd, args, { stdio: "inherit" });
7687
8705
  };
7688
8706
  function tryAcquireLock(path) {
7689
- mkdirSync14(dirname10(path), { recursive: true, mode: 448 });
8707
+ mkdirSync18(dirname13(path), { recursive: true, mode: 448 });
7690
8708
  const claim = () => {
7691
- const fd = openSync2(path, "wx", 384);
7692
- writeSync(fd, String(process.pid));
8709
+ const fd = openSync3(path, "wx", 384);
8710
+ writeSync2(fd, String(process.pid));
7693
8711
  return fd;
7694
8712
  };
7695
8713
  try {
@@ -7700,7 +8718,7 @@ function tryAcquireLock(path) {
7700
8718
  }
7701
8719
  let holderPid = 0;
7702
8720
  try {
7703
- holderPid = Number(readFileSync24(path, "utf-8").trim()) || 0;
8721
+ holderPid = Number(readFileSync28(path, "utf-8").trim()) || 0;
7704
8722
  } catch {
7705
8723
  try {
7706
8724
  return claim();
@@ -7717,7 +8735,7 @@ function tryAcquireLock(path) {
7717
8735
  }
7718
8736
  }
7719
8737
  try {
7720
- unlinkSync11(path);
8738
+ unlinkSync12(path);
7721
8739
  } catch {
7722
8740
  }
7723
8741
  try {
@@ -7729,11 +8747,11 @@ function tryAcquireLock(path) {
7729
8747
  }
7730
8748
  function releaseLock(fd, path) {
7731
8749
  try {
7732
- closeSync2(fd);
8750
+ closeSync3(fd);
7733
8751
  } catch {
7734
8752
  }
7735
8753
  try {
7736
- unlinkSync11(path);
8754
+ unlinkSync12(path);
7737
8755
  } catch {
7738
8756
  }
7739
8757
  }
@@ -7751,7 +8769,7 @@ async function runUpdate(opts = {}) {
7751
8769
  }
7752
8770
  log(`Update available: ${current} \u2192 ${latest}`);
7753
8771
  const detected = opts.installKindOverride ?? detectInstallKind();
7754
- const spawn2 = opts.spawn ?? defaultSpawn;
8772
+ const spawn3 = opts.spawn ?? defaultSpawn;
7755
8773
  switch (detected.kind) {
7756
8774
  case "npm-global": {
7757
8775
  if (opts.dryRun) {
@@ -7766,7 +8784,7 @@ async function runUpdate(opts = {}) {
7766
8784
  try {
7767
8785
  log(`Upgrading via npm\u2026`);
7768
8786
  try {
7769
- spawn2("npm", ["install", "-g", `${PKG_NAME}@latest`]);
8787
+ spawn3("npm", ["install", "-g", `${PKG_NAME}@latest`]);
7770
8788
  } catch (e) {
7771
8789
  warn(`npm install failed: ${e.message}`);
7772
8790
  warn(`Try running it manually: npm install -g ${PKG_NAME}@latest`);
@@ -7775,7 +8793,7 @@ async function runUpdate(opts = {}) {
7775
8793
  log(``);
7776
8794
  log(`Refreshing agent bundles\u2026`);
7777
8795
  try {
7778
- spawn2("hivemind", ["install", "--skip-auth"]);
8796
+ spawn3("hivemind", ["install", "--skip-auth"]);
7779
8797
  } catch (e) {
7780
8798
  warn(`Agent refresh failed: ${e.message}`);
7781
8799
  warn(`Run manually: hivemind install`);
@@ -7839,7 +8857,7 @@ var AUTH_SUBCOMMANDS = /* @__PURE__ */ new Set([
7839
8857
  "autoupdate",
7840
8858
  "sessions"
7841
8859
  ]);
7842
- var USAGE = `
8860
+ var USAGE2 = `
7843
8861
  hivemind \u2014 one brain for every agent on your team
7844
8862
 
7845
8863
  Usage:
@@ -7869,6 +8887,18 @@ Usage:
7869
8887
  Check npm for a newer @deeplake/hivemind, upgrade the CLI, and refresh
7870
8888
  every detected agent bundle. Single command for all agents.
7871
8889
 
8890
+ hivemind dashboard [--cwd <path>] [--out <path>] [--no-open]
8891
+ [--serve] [--port <n>]
8892
+ Build a self-contained HTML dashboard for this repo. Combines
8893
+ KPI cards (tokens saved, skills created, memory recalls,
8894
+ sessions) with the codebase-graph visualization. Writes to
8895
+ ~/.hivemind/dashboards/<repo-key>/index.html by default.
8896
+ --no-open skips the browser launch (headless / CI scenarios).
8897
+ --serve starts a loopback HTTP server at http://127.0.0.1:<port>
8898
+ (default 8123) so the dashboard is reachable via a URL \u2014 useful
8899
+ over SSH; VS Code / Cursor Remote-SSH auto-forwards the port
8900
+ and opens it in the integrated Simple Browser tab on click.
8901
+
7872
8902
  Semantic search (embeddings):
7873
8903
  hivemind embeddings install Download @huggingface/transformers
7874
8904
  once (~600 MB) into a shared dir,
@@ -8087,7 +9117,7 @@ async function main() {
8087
9117
  const args = process.argv.slice(2);
8088
9118
  const cmd = args[0];
8089
9119
  if (!cmd || cmd === "--help" || cmd === "-h" || cmd === "help") {
8090
- log(USAGE);
9120
+ log(USAGE2);
8091
9121
  return;
8092
9122
  }
8093
9123
  if (cmd === "--version" || cmd === "-v" || cmd === "version") {
@@ -8121,6 +9151,10 @@ async function main() {
8121
9151
  runSkillifyCommand(args.slice(1));
8122
9152
  return;
8123
9153
  }
9154
+ if (cmd === "dashboard") {
9155
+ const code = await runDashboardCommand(args.slice(1));
9156
+ process.exit(code);
9157
+ }
8124
9158
  if (cmd === "embeddings") {
8125
9159
  const sub = args[1];
8126
9160
  if (sub === "install") {
@@ -8168,7 +9202,7 @@ async function main() {
8168
9202
  return;
8169
9203
  }
8170
9204
  warn(`Unknown command: ${cmd}`);
8171
- log(USAGE);
9205
+ log(USAGE2);
8172
9206
  process.exit(1);
8173
9207
  }
8174
9208
  main().catch((err) => {