@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/.claude-plugin/marketplace.json +3 -3
- package/.claude-plugin/plugin.json +1 -1
- package/bundle/cli.js +1230 -196
- 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/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((
|
|
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
|
-
|
|
152
|
+
resolve3(defaultYes);
|
|
153
153
|
else
|
|
154
|
-
|
|
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((
|
|
160
|
+
return new Promise((resolve3) => {
|
|
161
161
|
rl.question(message, (answer) => {
|
|
162
162
|
rl.close();
|
|
163
|
-
|
|
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((
|
|
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((
|
|
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/
|
|
5421
|
-
import {
|
|
5422
|
-
import { homedir as
|
|
5423
|
-
import { dirname as
|
|
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/
|
|
5426
|
-
import { existsSync as
|
|
5427
|
-
import {
|
|
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
|
|
5431
|
-
import { dirname as
|
|
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
|
|
5435
|
-
import { join as
|
|
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 :
|
|
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 =
|
|
5452
|
-
if (!
|
|
5628
|
+
const legacy = join23(dirname5(current), "skilify");
|
|
5629
|
+
if (!existsSync17(legacy))
|
|
5453
5630
|
return;
|
|
5454
|
-
if (
|
|
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, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
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
|
|
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 (!
|
|
6495
|
+
if (!existsSync20(CONFIG_PATH2))
|
|
5478
6496
|
return DEFAULT;
|
|
5479
6497
|
try {
|
|
5480
|
-
const raw = JSON.parse(
|
|
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
|
-
|
|
5492
|
-
|
|
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
|
|
5497
|
-
import { homedir as
|
|
5498
|
-
import { dirname as
|
|
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
|
|
5502
|
-
import { homedir as
|
|
5503
|
-
import { join as
|
|
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
|
|
6537
|
+
return join29(skillsRoot, name);
|
|
5520
6538
|
}
|
|
5521
6539
|
function skillPath(skillsRoot, name) {
|
|
5522
|
-
return
|
|
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 (
|
|
6618
|
+
if (existsSync21(path)) {
|
|
5601
6619
|
throw new Error(`skill already exists at ${path}; use mergeSkill`);
|
|
5602
6620
|
}
|
|
5603
|
-
|
|
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
|
-
|
|
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 (!
|
|
6653
|
+
if (!existsSync21(skillsRoot))
|
|
5636
6654
|
return [];
|
|
5637
6655
|
const out = [];
|
|
5638
|
-
for (const name of
|
|
5639
|
-
const skillFile =
|
|
5640
|
-
if (
|
|
5641
|
-
out.push({ name, body:
|
|
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
|
|
6666
|
+
return join29(homedir15(), ".claude", "skills");
|
|
5649
6667
|
}
|
|
5650
|
-
return
|
|
6668
|
+
return join29(cwd, ".claude", "skills");
|
|
5651
6669
|
}
|
|
5652
6670
|
|
|
5653
6671
|
// dist/src/skillify/manifest.js
|
|
5654
|
-
import { existsSync as
|
|
5655
|
-
import { dirname as
|
|
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
|
|
6678
|
+
return join30(getStateDir(), "pulled.json");
|
|
5661
6679
|
}
|
|
5662
6680
|
function loadManifest(path = manifestPath()) {
|
|
5663
6681
|
migrateLegacyStateDir();
|
|
5664
|
-
if (!
|
|
6682
|
+
if (!existsSync22(path))
|
|
5665
6683
|
return emptyManifest();
|
|
5666
6684
|
let raw;
|
|
5667
6685
|
try {
|
|
5668
|
-
raw =
|
|
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
|
-
|
|
6733
|
+
mkdirSync13(dirname7(path), { recursive: true });
|
|
5716
6734
|
const tmp = `${path}.tmp`;
|
|
5717
|
-
|
|
5718
|
-
|
|
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 =
|
|
6761
|
+
st = lstatSync4(path);
|
|
5744
6762
|
} catch {
|
|
5745
6763
|
continue;
|
|
5746
6764
|
}
|
|
5747
6765
|
if (!st.isSymbolicLink())
|
|
5748
6766
|
continue;
|
|
5749
6767
|
try {
|
|
5750
|
-
|
|
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 (
|
|
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
|
|
5774
|
-
import { homedir as
|
|
5775
|
-
import { join as
|
|
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 =
|
|
5779
|
-
const piInstalled =
|
|
5780
|
-
const hermesInstalled =
|
|
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(
|
|
6800
|
+
out.push(join31(home, ".agents", "skills"));
|
|
5783
6801
|
}
|
|
5784
6802
|
if (hermesInstalled) {
|
|
5785
|
-
out.push(
|
|
6803
|
+
out.push(join31(home, ".hermes", "skills"));
|
|
5786
6804
|
}
|
|
5787
6805
|
if (piInstalled) {
|
|
5788
|
-
out.push(
|
|
6806
|
+
out.push(join31(home, ".pi", "agent", "skills"));
|
|
5789
6807
|
}
|
|
5790
6808
|
return out;
|
|
5791
6809
|
}
|
|
5792
|
-
function detectAgentSkillsRoots(canonicalRoot, home =
|
|
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
|
|
6854
|
+
return join32(homedir17(), ".claude", "skills");
|
|
5837
6855
|
if (!cwd)
|
|
5838
6856
|
throw new Error("install=project requires a cwd");
|
|
5839
|
-
return
|
|
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 =
|
|
6862
|
+
const link = join32(root, dirName);
|
|
5845
6863
|
let existing;
|
|
5846
6864
|
try {
|
|
5847
|
-
existing =
|
|
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
|
-
|
|
6884
|
+
unlinkSync10(link);
|
|
5867
6885
|
} catch {
|
|
5868
6886
|
continue;
|
|
5869
6887
|
}
|
|
5870
6888
|
}
|
|
5871
6889
|
try {
|
|
5872
|
-
|
|
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 =
|
|
5888
|
-
if (!
|
|
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 (!
|
|
7016
|
+
if (!existsSync24(path))
|
|
5999
7017
|
return null;
|
|
6000
7018
|
try {
|
|
6001
|
-
const text =
|
|
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 =
|
|
6097
|
-
const skillFile =
|
|
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
|
-
|
|
6109
|
-
if (
|
|
7126
|
+
mkdirSync14(skillDir2, { recursive: true });
|
|
7127
|
+
if (existsSync24(skillFile)) {
|
|
6110
7128
|
try {
|
|
6111
|
-
|
|
7129
|
+
renameSync7(skillFile, `${skillFile}.bak`);
|
|
6112
7130
|
} catch {
|
|
6113
7131
|
}
|
|
6114
7132
|
}
|
|
6115
|
-
|
|
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
|
|
6158
|
-
import { homedir as
|
|
6159
|
-
import { join as
|
|
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
|
|
7180
|
+
return join33(homedir18(), ".claude", "skills");
|
|
6163
7181
|
if (!cwd)
|
|
6164
7182
|
throw new Error("cwd required when install === 'project'");
|
|
6165
|
-
return
|
|
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 =
|
|
6189
|
-
if (!
|
|
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 (
|
|
7260
|
+
if (existsSync25(root) && (opts.all || opts.legacyCleanup)) {
|
|
6243
7261
|
const manifestDirNames = new Set(entries.map((e) => e.dirName));
|
|
6244
|
-
for (const dirName of
|
|
7262
|
+
for (const dirName of readdirSync5(root)) {
|
|
6245
7263
|
if (manifestDirNames.has(dirName))
|
|
6246
7264
|
continue;
|
|
6247
|
-
const path =
|
|
7265
|
+
const path = join33(root, dirName);
|
|
6248
7266
|
let st;
|
|
6249
7267
|
try {
|
|
6250
|
-
st =
|
|
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
|
|
6327
|
-
import { homedir as
|
|
6328
|
-
import { basename, dirname as
|
|
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
|
|
6332
|
-
import { homedir as
|
|
6333
|
-
import { join as
|
|
6334
|
-
var HOME2 =
|
|
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 =
|
|
6341
|
-
if (
|
|
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 =
|
|
6349
|
-
if (
|
|
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 =
|
|
7389
|
+
subdirs = readdirSync6(install.sessionRoot);
|
|
6372
7390
|
} catch {
|
|
6373
7391
|
continue;
|
|
6374
7392
|
}
|
|
6375
7393
|
for (const sub of subdirs) {
|
|
6376
|
-
const subdirPath =
|
|
7394
|
+
const subdirPath = join34(install.sessionRoot, sub);
|
|
6377
7395
|
try {
|
|
6378
|
-
if (!
|
|
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 =
|
|
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 =
|
|
7411
|
+
const fullPath = join34(subdirPath, f);
|
|
6394
7412
|
let stats;
|
|
6395
7413
|
try {
|
|
6396
|
-
stats =
|
|
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 =
|
|
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
|
|
7562
|
+
import { existsSync as existsSync27 } from "node:fs";
|
|
6545
7563
|
import { createRequire } from "node:module";
|
|
6546
|
-
import { homedir as
|
|
6547
|
-
import { join as
|
|
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 (
|
|
7571
|
+
if (existsSync27(c))
|
|
6554
7572
|
return c;
|
|
6555
7573
|
}
|
|
6556
7574
|
return null;
|
|
6557
7575
|
}
|
|
6558
7576
|
function findAgentBin(agent) {
|
|
6559
|
-
const home =
|
|
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
|
-
|
|
7586
|
+
join35(home, ".claude", "local", "claude"),
|
|
6569
7587
|
"/usr/local/bin/claude",
|
|
6570
7588
|
"/usr/bin/claude",
|
|
6571
|
-
|
|
6572
|
-
|
|
7589
|
+
join35(home, ".npm-global", "bin", "claude"),
|
|
7590
|
+
join35(home, ".local", "bin", "claude"),
|
|
6573
7591
|
"/opt/homebrew/bin/claude"
|
|
6574
|
-
]) ??
|
|
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
|
-
|
|
6580
|
-
|
|
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
|
-
|
|
6588
|
-
|
|
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
|
-
|
|
7611
|
+
join35(home, ".local", "bin", "hermes"),
|
|
6594
7612
|
"/usr/local/bin/hermes",
|
|
6595
7613
|
"/usr/bin/hermes",
|
|
6596
|
-
|
|
7614
|
+
join35(home, ".npm-global", "bin", "hermes"),
|
|
6597
7615
|
"/opt/homebrew/bin/hermes"
|
|
6598
|
-
]) ??
|
|
7616
|
+
]) ?? join35(home, ".local", "bin", "hermes");
|
|
6599
7617
|
case "pi":
|
|
6600
7618
|
return firstExistingPath([
|
|
6601
|
-
|
|
7619
|
+
join35(home, ".local", "bin", "pi"),
|
|
6602
7620
|
"/usr/local/bin/pi",
|
|
6603
7621
|
"/usr/bin/pi",
|
|
6604
|
-
|
|
7622
|
+
join35(home, ".npm-global", "bin", "pi"),
|
|
6605
7623
|
"/opt/homebrew/bin/pi"
|
|
6606
|
-
]) ??
|
|
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
|
|
6637
|
-
import { homedir as
|
|
6638
|
-
import { dirname as
|
|
6639
|
-
var LOCAL_MANIFEST_PATH =
|
|
6640
|
-
var LOCAL_MINE_LOCK_PATH =
|
|
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 (!
|
|
7660
|
+
if (!existsSync28(path))
|
|
6643
7661
|
return null;
|
|
6644
7662
|
try {
|
|
6645
|
-
return JSON.parse(
|
|
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
|
-
|
|
6652
|
-
|
|
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
|
|
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((
|
|
7686
|
+
return new Promise((resolve3) => {
|
|
6669
7687
|
if (opts.agent !== "claude_code") {
|
|
6670
|
-
|
|
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 (!
|
|
6679
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
7043
|
-
|
|
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 =
|
|
7055
|
-
|
|
7056
|
-
const verdictPath =
|
|
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
|
-
|
|
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
|
-
|
|
8079
|
+
writeFileSync19(join37(sessionTmp, "gate-stdout.txt"), gate.stdout);
|
|
7062
8080
|
if (gate.stderr)
|
|
7063
|
-
|
|
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 =
|
|
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 =
|
|
7123
|
-
const symlinks = fanOutRoots.length > 0 ? fanOutSymlinks(canonicalDir,
|
|
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 (!
|
|
8317
|
+
if (!existsSync30(dir)) {
|
|
7300
8318
|
console.log(`state: (no projects tracked yet)`);
|
|
7301
8319
|
return;
|
|
7302
8320
|
}
|
|
7303
|
-
const files =
|
|
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(
|
|
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" ?
|
|
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 =
|
|
7347
|
-
const globalPath =
|
|
7348
|
-
if (!
|
|
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 (
|
|
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
|
-
|
|
7357
|
-
|
|
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" ?
|
|
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" ?
|
|
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
|
|
7610
|
-
import { homedir as
|
|
7611
|
-
import { dirname as
|
|
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
|
|
7616
|
-
import { dirname as
|
|
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
|
|
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 =
|
|
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(
|
|
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 =
|
|
8668
|
+
const parent = dirname13(dir);
|
|
7651
8669
|
if (parent === dir)
|
|
7652
8670
|
break;
|
|
7653
8671
|
dir = parent;
|
|
7654
8672
|
}
|
|
7655
|
-
installDir ??=
|
|
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 (
|
|
8682
|
+
if (existsSync31(`${gitDir}${sep}.git`)) {
|
|
7665
8683
|
return { kind: "local-dev", installDir };
|
|
7666
8684
|
}
|
|
7667
|
-
const parent =
|
|
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
|
-
|
|
8707
|
+
mkdirSync18(dirname13(path), { recursive: true, mode: 448 });
|
|
7690
8708
|
const claim = () => {
|
|
7691
|
-
const fd =
|
|
7692
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
8750
|
+
closeSync3(fd);
|
|
7733
8751
|
} catch {
|
|
7734
8752
|
}
|
|
7735
8753
|
try {
|
|
7736
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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(
|
|
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(
|
|
9205
|
+
log(USAGE2);
|
|
8172
9206
|
process.exit(1);
|
|
8173
9207
|
}
|
|
8174
9208
|
main().catch((err) => {
|