@deeplake/hivemind 0.7.45 → 0.7.46
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 +2215 -341
- package/codex/bundle/capture.js +43 -0
- package/codex/bundle/commands/auth-login.js +43 -0
- package/codex/bundle/graph-pull-worker.js +1185 -0
- package/codex/bundle/pre-tool-use.js +43 -0
- package/codex/bundle/session-start-setup.js +43 -0
- package/codex/bundle/session-start.js +70 -4
- package/codex/bundle/shell/deeplake-shell.js +577 -25
- package/codex/bundle/skillify-worker.js +30 -3
- package/codex/bundle/stop.js +97 -50
- package/cursor/bundle/capture.js +97 -50
- package/cursor/bundle/commands/auth-login.js +43 -0
- package/cursor/bundle/graph-pull-worker.js +1185 -0
- package/cursor/bundle/pre-tool-use.js +43 -0
- package/cursor/bundle/session-end.js +49 -44
- package/cursor/bundle/session-start.js +66 -0
- package/cursor/bundle/shell/deeplake-shell.js +577 -25
- package/cursor/bundle/skillify-worker.js +30 -3
- package/hermes/bundle/capture.js +97 -50
- package/hermes/bundle/commands/auth-login.js +43 -0
- package/hermes/bundle/graph-pull-worker.js +1185 -0
- package/hermes/bundle/pre-tool-use.js +43 -0
- package/hermes/bundle/session-end.js +49 -44
- package/hermes/bundle/session-start.js +66 -0
- package/hermes/bundle/shell/deeplake-shell.js +577 -25
- package/hermes/bundle/skillify-worker.js +30 -3
- package/mcp/bundle/server.js +43 -0
- package/openclaw/dist/chunks/{config-XEK4MJJS.js → config-O5PDJQ7Y.js} +1 -0
- package/openclaw/dist/index.js +47 -2
- package/openclaw/dist/skillify-worker.js +30 -3
- package/openclaw/openclaw.plugin.json +1 -1
- package/openclaw/package.json +1 -1
- package/package.json +3 -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((resolve6) => {
|
|
148
148
|
rl.question(`${message} ${hint} `, (answer) => {
|
|
149
149
|
rl.close();
|
|
150
150
|
const a = answer.trim().toLowerCase();
|
|
151
151
|
if (a === "")
|
|
152
|
-
|
|
152
|
+
resolve6(defaultYes);
|
|
153
153
|
else
|
|
154
|
-
|
|
154
|
+
resolve6(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((resolve6) => {
|
|
161
161
|
rl.question(message, (answer) => {
|
|
162
162
|
rl.close();
|
|
163
|
-
|
|
163
|
+
resolve6(answer.trim());
|
|
164
164
|
});
|
|
165
165
|
});
|
|
166
166
|
}
|
|
@@ -4431,6 +4431,7 @@ function loadConfig() {
|
|
|
4431
4431
|
tableName: process.env.HIVEMIND_TABLE ?? "memory",
|
|
4432
4432
|
sessionsTableName: process.env.HIVEMIND_SESSIONS_TABLE ?? "sessions",
|
|
4433
4433
|
skillsTableName: process.env.HIVEMIND_SKILLS_TABLE ?? "skills",
|
|
4434
|
+
codebaseTableName: process.env.HIVEMIND_CODEBASE_TABLE ?? "codebase",
|
|
4434
4435
|
memoryPath: process.env.HIVEMIND_MEMORY_PATH ?? join16(home, ".deeplake", "memory")
|
|
4435
4436
|
};
|
|
4436
4437
|
}
|
|
@@ -4536,9 +4537,33 @@ function validateSchema(label, cols) {
|
|
|
4536
4537
|
}
|
|
4537
4538
|
}
|
|
4538
4539
|
}
|
|
4540
|
+
var CODEBASE_COLUMNS = Object.freeze([
|
|
4541
|
+
// Identity key (matches the PK below)
|
|
4542
|
+
{ name: "org_id", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
4543
|
+
{ name: "workspace_id", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
4544
|
+
{ name: "repo_slug", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
4545
|
+
{ name: "user_id", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
4546
|
+
{ name: "worktree_id", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
4547
|
+
{ name: "commit_sha", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
4548
|
+
// Observation metadata
|
|
4549
|
+
{ name: "parent_sha", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
4550
|
+
{ name: "branch", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
4551
|
+
{ name: "ts", sql: "TIMESTAMP" },
|
|
4552
|
+
{ name: "pushed_by", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
4553
|
+
// Snapshot payload
|
|
4554
|
+
{ name: "snapshot_sha256", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
4555
|
+
{ name: "snapshot_jsonb", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
4556
|
+
{ name: "node_count", sql: "BIGINT NOT NULL DEFAULT 0" },
|
|
4557
|
+
{ name: "edge_count", sql: "BIGINT NOT NULL DEFAULT 0" },
|
|
4558
|
+
// Generator metadata (for drift diagnostics — what hivemind version produced this?)
|
|
4559
|
+
{ name: "generator", sql: "TEXT NOT NULL DEFAULT 'hivemind-graph'" },
|
|
4560
|
+
{ name: "generator_version", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
4561
|
+
{ name: "schema_version", sql: "BIGINT NOT NULL DEFAULT 1" }
|
|
4562
|
+
]);
|
|
4539
4563
|
validateSchema("MEMORY_COLUMNS", MEMORY_COLUMNS);
|
|
4540
4564
|
validateSchema("SESSIONS_COLUMNS", SESSIONS_COLUMNS);
|
|
4541
4565
|
validateSchema("SKILLS_COLUMNS", SKILLS_COLUMNS);
|
|
4566
|
+
validateSchema("CODEBASE_COLUMNS", CODEBASE_COLUMNS);
|
|
4542
4567
|
function buildCreateTableSql(tableName, cols) {
|
|
4543
4568
|
const safe = sqlIdent(tableName);
|
|
4544
4569
|
const colSql = cols.map((c) => `${c.name} ${c.sql}`).join(", ");
|
|
@@ -4745,7 +4770,7 @@ function getQueryTimeoutMs() {
|
|
|
4745
4770
|
return Number(process.env.HIVEMIND_QUERY_TIMEOUT_MS ?? 1e4);
|
|
4746
4771
|
}
|
|
4747
4772
|
function sleep2(ms) {
|
|
4748
|
-
return new Promise((
|
|
4773
|
+
return new Promise((resolve6) => setTimeout(resolve6, ms));
|
|
4749
4774
|
}
|
|
4750
4775
|
function isTimeoutError(error) {
|
|
4751
4776
|
const name = error instanceof Error ? error.name.toLowerCase() : "";
|
|
@@ -4775,7 +4800,7 @@ var Semaphore = class {
|
|
|
4775
4800
|
this.active++;
|
|
4776
4801
|
return;
|
|
4777
4802
|
}
|
|
4778
|
-
await new Promise((
|
|
4803
|
+
await new Promise((resolve6) => this.waiting.push(resolve6));
|
|
4779
4804
|
}
|
|
4780
4805
|
release() {
|
|
4781
4806
|
this.active--;
|
|
@@ -5085,6 +5110,24 @@ var DeeplakeApi = class {
|
|
|
5085
5110
|
* This sidesteps the Deeplake UPDATE-coalescing quirk that bit the wiki
|
|
5086
5111
|
* worker.
|
|
5087
5112
|
*/
|
|
5113
|
+
/**
|
|
5114
|
+
* Create the codebase table. One row per (org, workspace, repo, user,
|
|
5115
|
+
* worktree, commit) — see CODEBASE_COLUMNS for the schema. Healing
|
|
5116
|
+
* + index follow the same pattern as ensureSessionsTable.
|
|
5117
|
+
*/
|
|
5118
|
+
async ensureCodebaseTable(name) {
|
|
5119
|
+
const safe = sqlIdent(name);
|
|
5120
|
+
const tables = await this.listTables();
|
|
5121
|
+
if (!tables.includes(safe)) {
|
|
5122
|
+
log4(`table "${safe}" not found, creating`);
|
|
5123
|
+
await this.createTableWithRetry(buildCreateTableSql(safe, CODEBASE_COLUMNS), safe);
|
|
5124
|
+
log4(`table "${safe}" created`);
|
|
5125
|
+
if (!tables.includes(safe))
|
|
5126
|
+
this._tablesCache = [...tables, safe];
|
|
5127
|
+
}
|
|
5128
|
+
await this.healSchema(safe, CODEBASE_COLUMNS);
|
|
5129
|
+
await this.ensureLookupIndex(safe, "codebase_identity", `("org_id", "workspace_id", "repo_slug", "user_id", "worktree_id", "commit_sha")`);
|
|
5130
|
+
}
|
|
5088
5131
|
async ensureSkillsTable(name) {
|
|
5089
5132
|
const safe = sqlIdent(name);
|
|
5090
5133
|
const tables = await this.listTables();
|
|
@@ -5347,96 +5390,1950 @@ async function runAuthCommand(args) {
|
|
|
5347
5390
|
console.log(`Invited ${email} with ${mode} access`);
|
|
5348
5391
|
break;
|
|
5349
5392
|
}
|
|
5350
|
-
case "members": {
|
|
5351
|
-
if (!creds) {
|
|
5352
|
-
console.log("Not logged in.");
|
|
5353
|
-
process.exit(1);
|
|
5354
|
-
}
|
|
5355
|
-
const members = await listMembers(creds.token, creds.orgId, apiUrl);
|
|
5356
|
-
members.forEach((m) => console.log(`${m.role.padEnd(8)} ${m.email ?? m.name}`));
|
|
5393
|
+
case "members": {
|
|
5394
|
+
if (!creds) {
|
|
5395
|
+
console.log("Not logged in.");
|
|
5396
|
+
process.exit(1);
|
|
5397
|
+
}
|
|
5398
|
+
const members = await listMembers(creds.token, creds.orgId, apiUrl);
|
|
5399
|
+
members.forEach((m) => console.log(`${m.role.padEnd(8)} ${m.email ?? m.name}`));
|
|
5400
|
+
break;
|
|
5401
|
+
}
|
|
5402
|
+
case "remove": {
|
|
5403
|
+
if (!creds) {
|
|
5404
|
+
console.log("Not logged in.");
|
|
5405
|
+
process.exit(1);
|
|
5406
|
+
}
|
|
5407
|
+
const userId = args[1];
|
|
5408
|
+
if (!userId) {
|
|
5409
|
+
console.log("Usage: remove <user-id>");
|
|
5410
|
+
process.exit(1);
|
|
5411
|
+
}
|
|
5412
|
+
await removeMember(userId, creds.token, creds.orgId, apiUrl);
|
|
5413
|
+
console.log(`Removed user ${userId}`);
|
|
5414
|
+
break;
|
|
5415
|
+
}
|
|
5416
|
+
case "sessions": {
|
|
5417
|
+
const sub = args[1];
|
|
5418
|
+
if (sub === "prune") {
|
|
5419
|
+
await sessionPrune(args.slice(2));
|
|
5420
|
+
} else {
|
|
5421
|
+
console.log("Usage: sessions prune [--all | --before <date> | --session-id <id>] [--yes]");
|
|
5422
|
+
}
|
|
5423
|
+
break;
|
|
5424
|
+
}
|
|
5425
|
+
case "autoupdate": {
|
|
5426
|
+
if (!creds) {
|
|
5427
|
+
console.log("Not logged in.");
|
|
5428
|
+
process.exit(1);
|
|
5429
|
+
}
|
|
5430
|
+
const val = args[1]?.toLowerCase();
|
|
5431
|
+
if (val === "on" || val === "true") {
|
|
5432
|
+
saveCredentials({ ...creds, autoupdate: true });
|
|
5433
|
+
console.log("Autoupdate enabled. Plugin will update automatically on session start.");
|
|
5434
|
+
} else if (val === "off" || val === "false") {
|
|
5435
|
+
saveCredentials({ ...creds, autoupdate: false });
|
|
5436
|
+
console.log("Autoupdate disabled. You'll see a notice when updates are available.");
|
|
5437
|
+
} else {
|
|
5438
|
+
const current = creds.autoupdate !== false ? "on" : "off";
|
|
5439
|
+
console.log(`Autoupdate is currently: ${current}`);
|
|
5440
|
+
console.log("Usage: autoupdate [on|off]");
|
|
5441
|
+
}
|
|
5442
|
+
break;
|
|
5443
|
+
}
|
|
5444
|
+
case "logout": {
|
|
5445
|
+
if (deleteCredentials()) {
|
|
5446
|
+
console.log("Logged out. Credentials removed.");
|
|
5447
|
+
} else {
|
|
5448
|
+
console.log("Not logged in.");
|
|
5449
|
+
}
|
|
5450
|
+
break;
|
|
5451
|
+
}
|
|
5452
|
+
default:
|
|
5453
|
+
console.log("Commands: login, logout, whoami, org list, org switch, workspaces, workspace, sessions prune, invite, members, remove, autoupdate");
|
|
5454
|
+
}
|
|
5455
|
+
}
|
|
5456
|
+
if (process.argv[1] && process.argv[1].endsWith("auth-login.js")) {
|
|
5457
|
+
runAuthCommand(process.argv.slice(2)).catch((e) => {
|
|
5458
|
+
console.error(e.message);
|
|
5459
|
+
process.exit(1);
|
|
5460
|
+
});
|
|
5461
|
+
}
|
|
5462
|
+
|
|
5463
|
+
// dist/src/commands/graph.js
|
|
5464
|
+
import { execSync as execSync3 } from "node:child_process";
|
|
5465
|
+
import { readFileSync as readFileSync20, readdirSync as readdirSync2 } from "node:fs";
|
|
5466
|
+
import { join as join27, relative, resolve as resolve4, sep } from "node:path";
|
|
5467
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
5468
|
+
|
|
5469
|
+
// dist/src/graph/cache.js
|
|
5470
|
+
import { createHash } from "node:crypto";
|
|
5471
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync7, readFileSync as readFileSync15, renameSync as renameSync4, writeFileSync as writeFileSync11 } from "node:fs";
|
|
5472
|
+
import { dirname as dirname3, join as join20 } from "node:path";
|
|
5473
|
+
var CACHE_SCHEMA_VERSION = 1;
|
|
5474
|
+
function fileContentHash(contents) {
|
|
5475
|
+
return createHash("sha256").update(contents).digest("hex");
|
|
5476
|
+
}
|
|
5477
|
+
function cacheDir(baseDir) {
|
|
5478
|
+
return join20(baseDir, ".cache");
|
|
5479
|
+
}
|
|
5480
|
+
function cachePath(baseDir, contentSha256) {
|
|
5481
|
+
return join20(cacheDir(baseDir), `${contentSha256}.json`);
|
|
5482
|
+
}
|
|
5483
|
+
function readCache(baseDir, contentSha256, relativePath) {
|
|
5484
|
+
const path = cachePath(baseDir, contentSha256);
|
|
5485
|
+
if (!existsSync15(path))
|
|
5486
|
+
return null;
|
|
5487
|
+
let raw;
|
|
5488
|
+
try {
|
|
5489
|
+
raw = readFileSync15(path, "utf8");
|
|
5490
|
+
} catch {
|
|
5491
|
+
return null;
|
|
5492
|
+
}
|
|
5493
|
+
let parsed;
|
|
5494
|
+
try {
|
|
5495
|
+
parsed = JSON.parse(raw);
|
|
5496
|
+
} catch {
|
|
5497
|
+
return null;
|
|
5498
|
+
}
|
|
5499
|
+
if (parsed === null || typeof parsed !== "object" || parsed.schema !== CACHE_SCHEMA_VERSION || parsed.content_sha256 !== contentSha256) {
|
|
5500
|
+
return null;
|
|
5501
|
+
}
|
|
5502
|
+
const cached = parsed.extraction;
|
|
5503
|
+
if (cached === void 0 || typeof cached !== "object" || !Array.isArray(cached.nodes) || !Array.isArray(cached.edges) || !Array.isArray(cached.parse_errors)) {
|
|
5504
|
+
return null;
|
|
5505
|
+
}
|
|
5506
|
+
if (!validateItems(cached)) {
|
|
5507
|
+
return null;
|
|
5508
|
+
}
|
|
5509
|
+
try {
|
|
5510
|
+
return rewriteSourceFile(cached, relativePath);
|
|
5511
|
+
} catch {
|
|
5512
|
+
return null;
|
|
5513
|
+
}
|
|
5514
|
+
}
|
|
5515
|
+
function validateItems(ex) {
|
|
5516
|
+
if (typeof ex.source_file !== "string")
|
|
5517
|
+
return false;
|
|
5518
|
+
if (typeof ex.language !== "string")
|
|
5519
|
+
return false;
|
|
5520
|
+
for (const n of ex.nodes) {
|
|
5521
|
+
if (n === null || typeof n !== "object")
|
|
5522
|
+
return false;
|
|
5523
|
+
if (typeof n.id !== "string")
|
|
5524
|
+
return false;
|
|
5525
|
+
if (typeof n.label !== "string")
|
|
5526
|
+
return false;
|
|
5527
|
+
if (typeof n.kind !== "string")
|
|
5528
|
+
return false;
|
|
5529
|
+
if (typeof n.source_file !== "string")
|
|
5530
|
+
return false;
|
|
5531
|
+
if (typeof n.source_location !== "string")
|
|
5532
|
+
return false;
|
|
5533
|
+
if (typeof n.language !== "string")
|
|
5534
|
+
return false;
|
|
5535
|
+
if (typeof n.exported !== "boolean")
|
|
5536
|
+
return false;
|
|
5537
|
+
}
|
|
5538
|
+
for (const e of ex.edges) {
|
|
5539
|
+
if (e === null || typeof e !== "object")
|
|
5540
|
+
return false;
|
|
5541
|
+
if (typeof e.source !== "string")
|
|
5542
|
+
return false;
|
|
5543
|
+
if (typeof e.target !== "string")
|
|
5544
|
+
return false;
|
|
5545
|
+
if (typeof e.relation !== "string")
|
|
5546
|
+
return false;
|
|
5547
|
+
if (typeof e.confidence !== "string")
|
|
5548
|
+
return false;
|
|
5549
|
+
if (e.ord !== void 0 && typeof e.ord !== "number")
|
|
5550
|
+
return false;
|
|
5551
|
+
}
|
|
5552
|
+
for (const p of ex.parse_errors) {
|
|
5553
|
+
if (p === null || typeof p !== "object")
|
|
5554
|
+
return false;
|
|
5555
|
+
if (typeof p.source_file !== "string")
|
|
5556
|
+
return false;
|
|
5557
|
+
if (typeof p.message !== "string")
|
|
5558
|
+
return false;
|
|
5559
|
+
if (p.location !== void 0 && typeof p.location !== "string")
|
|
5560
|
+
return false;
|
|
5561
|
+
}
|
|
5562
|
+
return true;
|
|
5563
|
+
}
|
|
5564
|
+
function writeCache(baseDir, contentSha256, extraction) {
|
|
5565
|
+
const entry = {
|
|
5566
|
+
schema: CACHE_SCHEMA_VERSION,
|
|
5567
|
+
content_sha256: contentSha256,
|
|
5568
|
+
extraction
|
|
5569
|
+
};
|
|
5570
|
+
const path = cachePath(baseDir, contentSha256);
|
|
5571
|
+
try {
|
|
5572
|
+
mkdirSync7(dirname3(path), { recursive: true });
|
|
5573
|
+
const tmp = `${path}.tmp.${process.pid}.${Date.now()}`;
|
|
5574
|
+
writeFileSync11(tmp, JSON.stringify(entry));
|
|
5575
|
+
renameSync4(tmp, path);
|
|
5576
|
+
} catch {
|
|
5577
|
+
}
|
|
5578
|
+
}
|
|
5579
|
+
function rewriteSourceFile(cached, newPath) {
|
|
5580
|
+
const oldPath = cached.source_file;
|
|
5581
|
+
if (oldPath === newPath) {
|
|
5582
|
+
return cached;
|
|
5583
|
+
}
|
|
5584
|
+
const swap = (id) => {
|
|
5585
|
+
if (id.startsWith(`${oldPath}:`))
|
|
5586
|
+
return `${newPath}${id.slice(oldPath.length)}`;
|
|
5587
|
+
if (id.startsWith(`unresolved:${oldPath}:`)) {
|
|
5588
|
+
return `unresolved:${newPath}${id.slice(`unresolved:${oldPath}`.length)}`;
|
|
5589
|
+
}
|
|
5590
|
+
return id;
|
|
5591
|
+
};
|
|
5592
|
+
return {
|
|
5593
|
+
source_file: newPath,
|
|
5594
|
+
language: cached.language,
|
|
5595
|
+
// The synthetic module node uses source_file as its `label` (see
|
|
5596
|
+
// makeModuleNode in the extractor). On a cache hit after a rename/copy
|
|
5597
|
+
// we already rewrite `id` + `source_file`, but were leaving `label`
|
|
5598
|
+
// pointing at the OLD path — the snapshot then disagreed with a
|
|
5599
|
+
// fresh (non-cached) extraction. Rewrite `label` for module nodes too.
|
|
5600
|
+
// CodeRabbit P1.
|
|
5601
|
+
nodes: cached.nodes.map((n) => ({
|
|
5602
|
+
...n,
|
|
5603
|
+
id: swap(n.id),
|
|
5604
|
+
label: n.kind === "module" ? newPath : n.label,
|
|
5605
|
+
source_file: newPath
|
|
5606
|
+
})),
|
|
5607
|
+
edges: cached.edges.map((e) => ({ ...e, source: swap(e.source), target: swap(e.target) })),
|
|
5608
|
+
parse_errors: cached.parse_errors.map((p) => ({ ...p, source_file: newPath }))
|
|
5609
|
+
};
|
|
5610
|
+
}
|
|
5611
|
+
|
|
5612
|
+
// dist/src/graph/deeplake-push.js
|
|
5613
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
5614
|
+
async function pushSnapshot(snapshot, worktreeId, deps = {}) {
|
|
5615
|
+
if (process.env.HIVEMIND_GRAPH_PUSH === "0") {
|
|
5616
|
+
return { kind: "skipped-disabled" };
|
|
5617
|
+
}
|
|
5618
|
+
const config = (deps.loadConfig ?? loadConfig)();
|
|
5619
|
+
if (config === null) {
|
|
5620
|
+
return { kind: "skipped-no-auth" };
|
|
5621
|
+
}
|
|
5622
|
+
const commitSha = snapshot.graph.commit_sha;
|
|
5623
|
+
if (commitSha === null) {
|
|
5624
|
+
return { kind: "skipped-no-commit" };
|
|
5625
|
+
}
|
|
5626
|
+
const api = (deps.makeApi ?? defaultMakeApi)(config);
|
|
5627
|
+
try {
|
|
5628
|
+
await api.ensureCodebaseTable(config.codebaseTableName);
|
|
5629
|
+
} catch (err) {
|
|
5630
|
+
return errorOutcome("ensureCodebaseTable", err);
|
|
5631
|
+
}
|
|
5632
|
+
const snapshotSha256 = computeSnapshotSha256(snapshot);
|
|
5633
|
+
const tableId = sqlIdent(config.codebaseTableName);
|
|
5634
|
+
const repoSlug = snapshot.graph.repo_key;
|
|
5635
|
+
const userId = config.userName;
|
|
5636
|
+
const selectSql = `SELECT snapshot_sha256 FROM "${tableId}" WHERE org_id = '${sqlStr(config.orgId)}' AND workspace_id = '${sqlStr(config.workspaceId)}' AND repo_slug = '${sqlStr(repoSlug)}' AND user_id = '${sqlStr(userId)}' AND worktree_id = '${sqlStr(worktreeId)}' AND commit_sha = '${sqlStr(commitSha)}'`;
|
|
5637
|
+
let existing;
|
|
5638
|
+
try {
|
|
5639
|
+
existing = await api.query(selectSql);
|
|
5640
|
+
} catch (err) {
|
|
5641
|
+
return errorOutcome("SELECT existing", err);
|
|
5642
|
+
}
|
|
5643
|
+
if (existing.length > 0) {
|
|
5644
|
+
const cloudSha = String(existing[0].snapshot_sha256 ?? "");
|
|
5645
|
+
if (cloudSha === snapshotSha256) {
|
|
5646
|
+
return { kind: "already-current", commitSha };
|
|
5647
|
+
}
|
|
5648
|
+
return {
|
|
5649
|
+
kind: "drift",
|
|
5650
|
+
commitSha,
|
|
5651
|
+
localSha256: snapshotSha256,
|
|
5652
|
+
cloudSha256: cloudSha
|
|
5653
|
+
};
|
|
5654
|
+
}
|
|
5655
|
+
const canonical = canonicalJSON(snapshot);
|
|
5656
|
+
const observation = snapshot.observation;
|
|
5657
|
+
const insertSql = `INSERT INTO "${tableId}" (org_id, workspace_id, repo_slug, user_id, worktree_id, commit_sha, parent_sha, branch, ts, pushed_by, snapshot_sha256, snapshot_jsonb, node_count, edge_count, generator, generator_version, schema_version) VALUES ('${sqlStr(config.orgId)}', '${sqlStr(config.workspaceId)}', '${sqlStr(repoSlug)}', '${sqlStr(userId)}', '${sqlStr(worktreeId)}', '${sqlStr(commitSha)}', '', '${sqlStr(observation.branch ?? "")}', '${sqlStr(observation.ts)}', '${sqlStr(userId)}', '${sqlStr(snapshotSha256)}', '${sqlStr(canonical)}', ${snapshot.nodes.length}, ${snapshot.links.length}, '${sqlStr(snapshot.graph.generator)}', '${sqlStr(observation.generator_version)}', ${snapshot.graph.schema_version})`;
|
|
5658
|
+
try {
|
|
5659
|
+
await api.query(insertSql);
|
|
5660
|
+
} catch (err) {
|
|
5661
|
+
return errorOutcome("INSERT", err);
|
|
5662
|
+
}
|
|
5663
|
+
try {
|
|
5664
|
+
const verify = await api.query(selectSql);
|
|
5665
|
+
if (verify.length > 1) {
|
|
5666
|
+
return { kind: "inserted-with-duplicate-race", commitSha, rowCount: verify.length };
|
|
5667
|
+
}
|
|
5668
|
+
} catch {
|
|
5669
|
+
}
|
|
5670
|
+
return { kind: "inserted", commitSha };
|
|
5671
|
+
}
|
|
5672
|
+
function defaultMakeApi(config) {
|
|
5673
|
+
return new DeeplakeApi(config.token, config.apiUrl, config.orgId, config.workspaceId, config.tableName);
|
|
5674
|
+
}
|
|
5675
|
+
function errorOutcome(stage, err) {
|
|
5676
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
5677
|
+
return { kind: "error", message: `${stage}: ${message}` };
|
|
5678
|
+
}
|
|
5679
|
+
function computeSnapshotSha256(snapshot) {
|
|
5680
|
+
const stable = {
|
|
5681
|
+
directed: snapshot.directed,
|
|
5682
|
+
multigraph: snapshot.multigraph,
|
|
5683
|
+
graph: snapshot.graph,
|
|
5684
|
+
nodes: snapshot.nodes,
|
|
5685
|
+
links: snapshot.links
|
|
5686
|
+
};
|
|
5687
|
+
return createHash2("sha256").update(canonicalJSON(stable)).digest("hex");
|
|
5688
|
+
}
|
|
5689
|
+
function canonicalJSON(value) {
|
|
5690
|
+
return JSON.stringify(value, (_key, v) => {
|
|
5691
|
+
if (v !== null && typeof v === "object" && !Array.isArray(v)) {
|
|
5692
|
+
const sorted = {};
|
|
5693
|
+
for (const k of Object.keys(v).sort()) {
|
|
5694
|
+
sorted[k] = v[k];
|
|
5695
|
+
}
|
|
5696
|
+
return sorted;
|
|
5697
|
+
}
|
|
5698
|
+
return v;
|
|
5699
|
+
});
|
|
5700
|
+
}
|
|
5701
|
+
|
|
5702
|
+
// dist/src/graph/deeplake-pull.js
|
|
5703
|
+
import { execFileSync as execFileSync4 } from "node:child_process";
|
|
5704
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
5705
|
+
import { existsSync as existsSync18, mkdirSync as mkdirSync11, renameSync as renameSync7, writeFileSync as writeFileSync14 } from "node:fs";
|
|
5706
|
+
import { dirname as dirname7, join as join24 } from "node:path";
|
|
5707
|
+
|
|
5708
|
+
// dist/src/utils/repo-identity.js
|
|
5709
|
+
import { execSync as execSync2 } from "node:child_process";
|
|
5710
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
5711
|
+
import { basename, resolve as resolve2 } from "node:path";
|
|
5712
|
+
var DEFAULT_PORTS = {
|
|
5713
|
+
http: "80",
|
|
5714
|
+
https: "443",
|
|
5715
|
+
ssh: "22",
|
|
5716
|
+
git: "9418"
|
|
5717
|
+
};
|
|
5718
|
+
function normalizeGitRemoteUrl(url) {
|
|
5719
|
+
let s = url.trim();
|
|
5720
|
+
const schemeMatch = s.match(/^([a-z][a-z0-9+.-]*):\/\//i);
|
|
5721
|
+
const scheme = schemeMatch ? schemeMatch[1].toLowerCase() : null;
|
|
5722
|
+
if (schemeMatch)
|
|
5723
|
+
s = s.slice(schemeMatch[0].length);
|
|
5724
|
+
if (!scheme) {
|
|
5725
|
+
const scp = s.match(/^(?:[^@/\s]+@)?([^:/\s]+):(.+)$/);
|
|
5726
|
+
if (scp)
|
|
5727
|
+
s = `${scp[1]}/${scp[2]}`;
|
|
5728
|
+
}
|
|
5729
|
+
s = s.replace(/^[^@/]+@/, "");
|
|
5730
|
+
if (scheme && DEFAULT_PORTS[scheme]) {
|
|
5731
|
+
s = s.replace(new RegExp(`^([^/]+):${DEFAULT_PORTS[scheme]}(/|$)`), "$1$2");
|
|
5732
|
+
}
|
|
5733
|
+
s = s.replace(/\.git\/?$/i, "");
|
|
5734
|
+
s = s.replace(/\/+$/, "");
|
|
5735
|
+
return s.toLowerCase();
|
|
5736
|
+
}
|
|
5737
|
+
function deriveProjectKey(cwd) {
|
|
5738
|
+
const absCwd = resolve2(cwd);
|
|
5739
|
+
const project = basename(absCwd) || "unknown";
|
|
5740
|
+
let signature = null;
|
|
5741
|
+
try {
|
|
5742
|
+
const raw = execSync2("git config --get remote.origin.url", {
|
|
5743
|
+
cwd: absCwd,
|
|
5744
|
+
encoding: "utf-8",
|
|
5745
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
5746
|
+
}).trim();
|
|
5747
|
+
signature = raw ? normalizeGitRemoteUrl(raw) : null;
|
|
5748
|
+
} catch {
|
|
5749
|
+
}
|
|
5750
|
+
const input = signature ?? absCwd;
|
|
5751
|
+
const key = createHash3("sha1").update(input).digest("hex").slice(0, 16);
|
|
5752
|
+
return { key, project };
|
|
5753
|
+
}
|
|
5754
|
+
|
|
5755
|
+
// dist/src/graph/last-build.js
|
|
5756
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync8, readFileSync as readFileSync16, renameSync as renameSync5, writeFileSync as writeFileSync12 } from "node:fs";
|
|
5757
|
+
import { dirname as dirname4, join as join21 } from "node:path";
|
|
5758
|
+
function lastBuildPath(baseDir, worktreeId) {
|
|
5759
|
+
if (worktreeId !== void 0) {
|
|
5760
|
+
return join21(baseDir, "worktrees", worktreeId, ".last-build.json");
|
|
5761
|
+
}
|
|
5762
|
+
return join21(baseDir, ".last-build.json");
|
|
5763
|
+
}
|
|
5764
|
+
function writeLastBuild(baseDir, state, worktreeId) {
|
|
5765
|
+
const path = lastBuildPath(baseDir, worktreeId);
|
|
5766
|
+
try {
|
|
5767
|
+
mkdirSync8(dirname4(path), { recursive: true });
|
|
5768
|
+
const tmp = `${path}.tmp.${process.pid}.${Date.now()}`;
|
|
5769
|
+
writeFileSync12(tmp, JSON.stringify(state));
|
|
5770
|
+
renameSync5(tmp, path);
|
|
5771
|
+
} catch {
|
|
5772
|
+
}
|
|
5773
|
+
}
|
|
5774
|
+
function readLastBuild(baseDir, worktreeId) {
|
|
5775
|
+
let path = lastBuildPath(baseDir, worktreeId);
|
|
5776
|
+
if (!existsSync16(path)) {
|
|
5777
|
+
if (worktreeId === void 0)
|
|
5778
|
+
return null;
|
|
5779
|
+
const legacy = lastBuildPath(baseDir, void 0);
|
|
5780
|
+
if (!existsSync16(legacy))
|
|
5781
|
+
return null;
|
|
5782
|
+
path = legacy;
|
|
5783
|
+
}
|
|
5784
|
+
let raw;
|
|
5785
|
+
try {
|
|
5786
|
+
raw = readFileSync16(path, "utf8");
|
|
5787
|
+
} catch {
|
|
5788
|
+
return null;
|
|
5789
|
+
}
|
|
5790
|
+
let parsed;
|
|
5791
|
+
try {
|
|
5792
|
+
parsed = JSON.parse(raw);
|
|
5793
|
+
} catch {
|
|
5794
|
+
return null;
|
|
5795
|
+
}
|
|
5796
|
+
if (parsed === null || typeof parsed !== "object")
|
|
5797
|
+
return null;
|
|
5798
|
+
const o = parsed;
|
|
5799
|
+
if (typeof o.ts !== "number" || !Number.isFinite(o.ts))
|
|
5800
|
+
return null;
|
|
5801
|
+
if (o.commit_sha !== null && typeof o.commit_sha !== "string")
|
|
5802
|
+
return null;
|
|
5803
|
+
if (typeof o.snapshot_sha256 !== "string")
|
|
5804
|
+
return null;
|
|
5805
|
+
const out = { ts: o.ts, commit_sha: o.commit_sha, snapshot_sha256: o.snapshot_sha256 };
|
|
5806
|
+
if (typeof o.node_count === "number" && Number.isFinite(o.node_count) && o.node_count >= 0) {
|
|
5807
|
+
out.node_count = o.node_count;
|
|
5808
|
+
}
|
|
5809
|
+
if (typeof o.edge_count === "number" && Number.isFinite(o.edge_count) && o.edge_count >= 0) {
|
|
5810
|
+
out.edge_count = o.edge_count;
|
|
5811
|
+
}
|
|
5812
|
+
return out;
|
|
5813
|
+
}
|
|
5814
|
+
|
|
5815
|
+
// dist/src/graph/history.js
|
|
5816
|
+
import { appendFileSync as appendFileSync2, existsSync as existsSync17, mkdirSync as mkdirSync9, readFileSync as readFileSync17 } from "node:fs";
|
|
5817
|
+
import { dirname as dirname5, join as join22 } from "node:path";
|
|
5818
|
+
function historyPath(baseDir) {
|
|
5819
|
+
return join22(baseDir, "history.jsonl");
|
|
5820
|
+
}
|
|
5821
|
+
function appendHistoryEntry(baseDir, entry) {
|
|
5822
|
+
const path = historyPath(baseDir);
|
|
5823
|
+
try {
|
|
5824
|
+
mkdirSync9(dirname5(path), { recursive: true });
|
|
5825
|
+
appendFileSync2(path, JSON.stringify(entry) + "\n");
|
|
5826
|
+
} catch {
|
|
5827
|
+
}
|
|
5828
|
+
}
|
|
5829
|
+
function entryFromSnapshot(snapshot, snapshot_sha256, trigger) {
|
|
5830
|
+
return {
|
|
5831
|
+
ts: snapshot.observation.ts,
|
|
5832
|
+
commit_sha: snapshot.graph.commit_sha,
|
|
5833
|
+
snapshot_sha256,
|
|
5834
|
+
node_count: snapshot.nodes.length,
|
|
5835
|
+
edge_count: snapshot.links.length,
|
|
5836
|
+
trigger
|
|
5837
|
+
};
|
|
5838
|
+
}
|
|
5839
|
+
function readHistoryTail(baseDir, n) {
|
|
5840
|
+
const path = historyPath(baseDir);
|
|
5841
|
+
if (!existsSync17(path))
|
|
5842
|
+
return [];
|
|
5843
|
+
let raw;
|
|
5844
|
+
try {
|
|
5845
|
+
raw = readFileSync17(path, "utf8");
|
|
5846
|
+
} catch {
|
|
5847
|
+
return [];
|
|
5848
|
+
}
|
|
5849
|
+
const lines = raw.split("\n").filter((l) => l.length > 0);
|
|
5850
|
+
const entries = [];
|
|
5851
|
+
for (let i = lines.length - 1; i >= 0 && entries.length < n; i--) {
|
|
5852
|
+
const parsed = parseLine(lines[i]);
|
|
5853
|
+
if (parsed !== null)
|
|
5854
|
+
entries.unshift(parsed);
|
|
5855
|
+
}
|
|
5856
|
+
return entries;
|
|
5857
|
+
}
|
|
5858
|
+
function countHistoryEntries(baseDir) {
|
|
5859
|
+
const path = historyPath(baseDir);
|
|
5860
|
+
if (!existsSync17(path))
|
|
5861
|
+
return 0;
|
|
5862
|
+
try {
|
|
5863
|
+
const raw = readFileSync17(path, "utf8");
|
|
5864
|
+
return raw.split("\n").filter((l) => l.length > 0).length;
|
|
5865
|
+
} catch {
|
|
5866
|
+
return 0;
|
|
5867
|
+
}
|
|
5868
|
+
}
|
|
5869
|
+
function parseLine(line) {
|
|
5870
|
+
let obj;
|
|
5871
|
+
try {
|
|
5872
|
+
obj = JSON.parse(line);
|
|
5873
|
+
} catch {
|
|
5874
|
+
return null;
|
|
5875
|
+
}
|
|
5876
|
+
if (obj === null || typeof obj !== "object")
|
|
5877
|
+
return null;
|
|
5878
|
+
const o = obj;
|
|
5879
|
+
if (typeof o.ts !== "string")
|
|
5880
|
+
return null;
|
|
5881
|
+
if (o.commit_sha !== null && typeof o.commit_sha !== "string")
|
|
5882
|
+
return null;
|
|
5883
|
+
if (typeof o.snapshot_sha256 !== "string")
|
|
5884
|
+
return null;
|
|
5885
|
+
if (typeof o.node_count !== "number")
|
|
5886
|
+
return null;
|
|
5887
|
+
if (typeof o.edge_count !== "number")
|
|
5888
|
+
return null;
|
|
5889
|
+
if (typeof o.trigger !== "string")
|
|
5890
|
+
return null;
|
|
5891
|
+
return {
|
|
5892
|
+
ts: o.ts,
|
|
5893
|
+
commit_sha: o.commit_sha,
|
|
5894
|
+
snapshot_sha256: o.snapshot_sha256,
|
|
5895
|
+
node_count: o.node_count,
|
|
5896
|
+
edge_count: o.edge_count,
|
|
5897
|
+
trigger: o.trigger
|
|
5898
|
+
};
|
|
5899
|
+
}
|
|
5900
|
+
|
|
5901
|
+
// dist/src/graph/snapshot.js
|
|
5902
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
5903
|
+
import { mkdirSync as mkdirSync10, renameSync as renameSync6, writeFileSync as writeFileSync13 } from "node:fs";
|
|
5904
|
+
import { homedir as homedir10 } from "node:os";
|
|
5905
|
+
import { dirname as dirname6, join as join23 } from "node:path";
|
|
5906
|
+
function graphsRoot() {
|
|
5907
|
+
return process.env.HIVEMIND_GRAPHS_HOME ?? join23(homedir10(), ".hivemind", "graphs");
|
|
5908
|
+
}
|
|
5909
|
+
function repoDir(repoKey) {
|
|
5910
|
+
return join23(graphsRoot(), repoKey);
|
|
5911
|
+
}
|
|
5912
|
+
function buildSnapshot(extractions, metadata, observation) {
|
|
5913
|
+
const nodes = [];
|
|
5914
|
+
const links = [];
|
|
5915
|
+
for (const ex of extractions) {
|
|
5916
|
+
for (const n of ex.nodes)
|
|
5917
|
+
nodes.push(n);
|
|
5918
|
+
for (const e of ex.edges)
|
|
5919
|
+
links.push(e);
|
|
5920
|
+
}
|
|
5921
|
+
nodes.sort(compareNodes);
|
|
5922
|
+
links.sort(compareEdges);
|
|
5923
|
+
return {
|
|
5924
|
+
directed: true,
|
|
5925
|
+
multigraph: true,
|
|
5926
|
+
graph: metadata,
|
|
5927
|
+
observation,
|
|
5928
|
+
nodes,
|
|
5929
|
+
links
|
|
5930
|
+
};
|
|
5931
|
+
}
|
|
5932
|
+
function compareNodes(a, b) {
|
|
5933
|
+
return cmp(a.id, b.id);
|
|
5934
|
+
}
|
|
5935
|
+
function compareEdges(a, b) {
|
|
5936
|
+
let c = cmp(a.source, b.source);
|
|
5937
|
+
if (c !== 0)
|
|
5938
|
+
return c;
|
|
5939
|
+
c = cmp(a.target, b.target);
|
|
5940
|
+
if (c !== 0)
|
|
5941
|
+
return c;
|
|
5942
|
+
c = cmp(a.relation, b.relation);
|
|
5943
|
+
if (c !== 0)
|
|
5944
|
+
return c;
|
|
5945
|
+
return (a.ord ?? 0) - (b.ord ?? 0);
|
|
5946
|
+
}
|
|
5947
|
+
function cmp(a, b) {
|
|
5948
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
5949
|
+
}
|
|
5950
|
+
function canonicalSnapshot(snapshot) {
|
|
5951
|
+
return canonicalJSON2(snapshot);
|
|
5952
|
+
}
|
|
5953
|
+
function computeSnapshotSha2562(snapshot) {
|
|
5954
|
+
const stable = {
|
|
5955
|
+
directed: snapshot.directed,
|
|
5956
|
+
multigraph: snapshot.multigraph,
|
|
5957
|
+
graph: snapshot.graph,
|
|
5958
|
+
nodes: snapshot.nodes,
|
|
5959
|
+
links: snapshot.links
|
|
5960
|
+
};
|
|
5961
|
+
return createHash4("sha256").update(canonicalJSON2(stable)).digest("hex");
|
|
5962
|
+
}
|
|
5963
|
+
function canonicalJSON2(value) {
|
|
5964
|
+
return JSON.stringify(value, (_key, v) => {
|
|
5965
|
+
if (v !== null && typeof v === "object" && !Array.isArray(v)) {
|
|
5966
|
+
const sorted = {};
|
|
5967
|
+
for (const k of Object.keys(v).sort()) {
|
|
5968
|
+
sorted[k] = v[k];
|
|
5969
|
+
}
|
|
5970
|
+
return sorted;
|
|
5971
|
+
}
|
|
5972
|
+
return v;
|
|
5973
|
+
});
|
|
5974
|
+
}
|
|
5975
|
+
function writeSnapshot(snapshot, baseDir, trigger = "unknown", worktreeId) {
|
|
5976
|
+
const sha256 = computeSnapshotSha2562(snapshot);
|
|
5977
|
+
const commitSha = snapshot.graph.commit_sha;
|
|
5978
|
+
const fileBase = commitSha ?? sha256;
|
|
5979
|
+
const snapshotsDir = join23(baseDir, "snapshots");
|
|
5980
|
+
const snapshotPath = join23(snapshotsDir, `${fileBase}.json`);
|
|
5981
|
+
const canonical = canonicalSnapshot(snapshot);
|
|
5982
|
+
writeFileAtomic(snapshotPath, canonical);
|
|
5983
|
+
const worktreeRoot = worktreeId !== void 0 ? join23(baseDir, "worktrees", worktreeId) : baseDir;
|
|
5984
|
+
let latestCommitPath = null;
|
|
5985
|
+
if (commitSha !== null) {
|
|
5986
|
+
latestCommitPath = join23(worktreeRoot, "latest-commit.txt");
|
|
5987
|
+
writeFileAtomic(latestCommitPath, `${commitSha}
|
|
5988
|
+
`);
|
|
5989
|
+
}
|
|
5990
|
+
writeLastBuild(baseDir, {
|
|
5991
|
+
ts: Date.now(),
|
|
5992
|
+
commit_sha: commitSha,
|
|
5993
|
+
snapshot_sha256: sha256,
|
|
5994
|
+
node_count: snapshot.nodes.length,
|
|
5995
|
+
edge_count: snapshot.links.length
|
|
5996
|
+
}, worktreeId);
|
|
5997
|
+
appendHistoryEntry(baseDir, entryFromSnapshot(snapshot, sha256, trigger));
|
|
5998
|
+
return { snapshotPath, latestCommitPath, snapshotSha256: sha256 };
|
|
5999
|
+
}
|
|
6000
|
+
function writeFileAtomic(filePath, contents) {
|
|
6001
|
+
mkdirSync10(dirname6(filePath), { recursive: true });
|
|
6002
|
+
const tmp = `${filePath}.tmp.${process.pid}.${Date.now()}`;
|
|
6003
|
+
writeFileSync13(tmp, contents);
|
|
6004
|
+
renameSync6(tmp, filePath);
|
|
6005
|
+
}
|
|
6006
|
+
|
|
6007
|
+
// dist/src/graph/deeplake-pull.js
|
|
6008
|
+
function workTreeIdFor(cwd) {
|
|
6009
|
+
return createHash5("sha256").update(cwd).digest("hex").slice(0, 16);
|
|
6010
|
+
}
|
|
6011
|
+
async function pullSnapshot(cwd, deps = {}) {
|
|
6012
|
+
if (process.env.HIVEMIND_GRAPH_PULL === "0") {
|
|
6013
|
+
return { kind: "skipped-disabled" };
|
|
6014
|
+
}
|
|
6015
|
+
const config = (deps.loadConfig ?? loadConfig)();
|
|
6016
|
+
if (config === null) {
|
|
6017
|
+
return { kind: "skipped-no-auth" };
|
|
6018
|
+
}
|
|
6019
|
+
const head = (deps.readHead ?? defaultReadHead)(cwd);
|
|
6020
|
+
if (head === null) {
|
|
6021
|
+
return { kind: "skipped-no-head" };
|
|
6022
|
+
}
|
|
6023
|
+
const api = (deps.makeApi ?? defaultMakeApi2)(config);
|
|
6024
|
+
try {
|
|
6025
|
+
await api.ensureCodebaseTable(config.codebaseTableName);
|
|
6026
|
+
} catch (err) {
|
|
6027
|
+
return errorOutcome2("ensureCodebaseTable", err);
|
|
6028
|
+
}
|
|
6029
|
+
const tableId = sqlIdent(config.codebaseTableName);
|
|
6030
|
+
const { key: repoKey } = deriveProjectKey(cwd);
|
|
6031
|
+
const selectSql = `SELECT snapshot_jsonb, snapshot_sha256, ts, node_count, edge_count, branch, generator_version, worktree_id FROM "${tableId}" WHERE org_id = '${sqlStr(config.orgId)}' AND workspace_id = '${sqlStr(config.workspaceId)}' AND repo_slug = '${sqlStr(repoKey)}' AND user_id = '${sqlStr(config.userName)}' AND commit_sha = '${sqlStr(head)}' ORDER BY ts DESC LIMIT 1`;
|
|
6032
|
+
let rows;
|
|
6033
|
+
try {
|
|
6034
|
+
rows = await api.query(selectSql);
|
|
6035
|
+
} catch (err) {
|
|
6036
|
+
return errorOutcome2("SELECT cloud row", err);
|
|
6037
|
+
}
|
|
6038
|
+
if (rows.length === 0) {
|
|
6039
|
+
return { kind: "no-cloud-row", commitSha: head };
|
|
6040
|
+
}
|
|
6041
|
+
const row = rows[0];
|
|
6042
|
+
const cloudSha256 = String(row.snapshot_sha256 ?? "").trim();
|
|
6043
|
+
const cloudPayload = coerceSnapshotPayload(row.snapshot_jsonb);
|
|
6044
|
+
if (cloudPayload === null) {
|
|
6045
|
+
return errorOutcome2("SELECT cloud row", new Error("invalid snapshot_jsonb payload"));
|
|
6046
|
+
}
|
|
6047
|
+
if (cloudSha256 !== "") {
|
|
6048
|
+
const computedSha = createHash5("sha256").update(cloudPayload).digest("hex");
|
|
6049
|
+
if (cloudSha256 !== computedSha) {
|
|
6050
|
+
return errorOutcome2("SELECT cloud row", new Error(`snapshot_sha256 mismatch (expected ${cloudSha256}, got ${computedSha})`));
|
|
6051
|
+
}
|
|
6052
|
+
}
|
|
6053
|
+
const cloudTs = parseTs(row.ts);
|
|
6054
|
+
const baseDir = repoDir(repoKey);
|
|
6055
|
+
const worktreeId = workTreeIdFor(cwd);
|
|
6056
|
+
const local = readLastBuild(baseDir, worktreeId);
|
|
6057
|
+
if (local !== null && local.commit_sha === head) {
|
|
6058
|
+
if (cloudSha256 !== "" && local.snapshot_sha256 === cloudSha256) {
|
|
6059
|
+
return { kind: "up-to-date", commitSha: head, snapshotSha256: cloudSha256 };
|
|
6060
|
+
}
|
|
6061
|
+
if (local.ts > cloudTs) {
|
|
6062
|
+
return {
|
|
6063
|
+
kind: "local-newer",
|
|
6064
|
+
commitSha: head,
|
|
6065
|
+
localTs: local.ts,
|
|
6066
|
+
cloudTs
|
|
6067
|
+
};
|
|
6068
|
+
}
|
|
6069
|
+
}
|
|
6070
|
+
const snapshotsDir = join24(baseDir, "snapshots");
|
|
6071
|
+
const snapshotPath = join24(snapshotsDir, `${head}.json`);
|
|
6072
|
+
const worktreeRoot = join24(baseDir, "worktrees", worktreeId);
|
|
6073
|
+
try {
|
|
6074
|
+
writeFileAtomic2(snapshotPath, cloudPayload);
|
|
6075
|
+
writeFileAtomic2(join24(worktreeRoot, "latest-commit.txt"), `${head}
|
|
6076
|
+
`);
|
|
6077
|
+
writeLastBuild(baseDir, {
|
|
6078
|
+
ts: cloudTs,
|
|
6079
|
+
commit_sha: head,
|
|
6080
|
+
snapshot_sha256: cloudSha256,
|
|
6081
|
+
node_count: numOrUndefined(row.node_count),
|
|
6082
|
+
edge_count: numOrUndefined(row.edge_count)
|
|
6083
|
+
}, worktreeId);
|
|
6084
|
+
appendHistoryEntry(baseDir, {
|
|
6085
|
+
ts: new Date(cloudTs).toISOString(),
|
|
6086
|
+
commit_sha: head,
|
|
6087
|
+
snapshot_sha256: cloudSha256,
|
|
6088
|
+
node_count: Number(row.node_count ?? 0),
|
|
6089
|
+
edge_count: Number(row.edge_count ?? 0),
|
|
6090
|
+
trigger: "pull"
|
|
6091
|
+
});
|
|
6092
|
+
} catch (err) {
|
|
6093
|
+
return errorOutcome2("write local files", err);
|
|
6094
|
+
}
|
|
6095
|
+
return {
|
|
6096
|
+
kind: "pulled",
|
|
6097
|
+
commitSha: head,
|
|
6098
|
+
snapshotSha256: cloudSha256,
|
|
6099
|
+
bytes: Buffer.byteLength(cloudPayload, "utf8"),
|
|
6100
|
+
cloudTs,
|
|
6101
|
+
sourceWorktreePath: String(row.worktree_id ?? "")
|
|
6102
|
+
};
|
|
6103
|
+
}
|
|
6104
|
+
function defaultReadHead(cwd) {
|
|
6105
|
+
try {
|
|
6106
|
+
return execFileSync4("git", ["rev-parse", "HEAD"], {
|
|
6107
|
+
cwd,
|
|
6108
|
+
encoding: "utf8",
|
|
6109
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
6110
|
+
}).trim();
|
|
6111
|
+
} catch {
|
|
6112
|
+
return null;
|
|
6113
|
+
}
|
|
6114
|
+
}
|
|
6115
|
+
function defaultMakeApi2(config) {
|
|
6116
|
+
return new DeeplakeApi(config.token, config.apiUrl, config.orgId, config.workspaceId, config.tableName);
|
|
6117
|
+
}
|
|
6118
|
+
function parseTs(raw) {
|
|
6119
|
+
if (typeof raw === "number" && Number.isFinite(raw)) {
|
|
6120
|
+
return raw < 1e12 ? raw * 1e3 : raw;
|
|
6121
|
+
}
|
|
6122
|
+
if (typeof raw === "string") {
|
|
6123
|
+
const parsed = Date.parse(raw);
|
|
6124
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
6125
|
+
}
|
|
6126
|
+
return 0;
|
|
6127
|
+
}
|
|
6128
|
+
function numOrUndefined(raw) {
|
|
6129
|
+
if (typeof raw === "number" && Number.isFinite(raw) && raw >= 0)
|
|
6130
|
+
return raw;
|
|
6131
|
+
if (typeof raw === "string") {
|
|
6132
|
+
const n = Number(raw);
|
|
6133
|
+
if (Number.isFinite(n) && n >= 0)
|
|
6134
|
+
return n;
|
|
6135
|
+
}
|
|
6136
|
+
return void 0;
|
|
6137
|
+
}
|
|
6138
|
+
function coerceSnapshotPayload(raw) {
|
|
6139
|
+
if (typeof raw === "string")
|
|
6140
|
+
return raw;
|
|
6141
|
+
if (raw !== null && typeof raw === "object")
|
|
6142
|
+
return JSON.stringify(raw);
|
|
6143
|
+
return null;
|
|
6144
|
+
}
|
|
6145
|
+
function writeFileAtomic2(filePath, contents) {
|
|
6146
|
+
mkdirSync11(dirname7(filePath), { recursive: true });
|
|
6147
|
+
const tmp = `${filePath}.tmp.${process.pid}.${Date.now()}`;
|
|
6148
|
+
writeFileSync14(tmp, contents);
|
|
6149
|
+
renameSync7(tmp, filePath);
|
|
6150
|
+
}
|
|
6151
|
+
function errorOutcome2(stage, err) {
|
|
6152
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
6153
|
+
return { kind: "error", message: `${stage}: ${message}` };
|
|
6154
|
+
}
|
|
6155
|
+
|
|
6156
|
+
// dist/src/graph/diff.js
|
|
6157
|
+
import { existsSync as existsSync19, readFileSync as readFileSync18 } from "node:fs";
|
|
6158
|
+
import { join as join25 } from "node:path";
|
|
6159
|
+
function edgeKey(e) {
|
|
6160
|
+
return `${e.source}${e.target}${e.relation}${e.ord ?? 0}`;
|
|
6161
|
+
}
|
|
6162
|
+
function diffSnapshots(from, to) {
|
|
6163
|
+
const fromNodeIds = new Set(from.nodes.map((n) => n.id));
|
|
6164
|
+
const toNodeIds = new Set(to.nodes.map((n) => n.id));
|
|
6165
|
+
const nodesAdded = to.nodes.filter((n) => !fromNodeIds.has(n.id));
|
|
6166
|
+
const nodesRemoved = from.nodes.filter((n) => !toNodeIds.has(n.id));
|
|
6167
|
+
const fromEdgeKeys = new Set(from.links.map(edgeKey));
|
|
6168
|
+
const toEdgeKeys = new Set(to.links.map(edgeKey));
|
|
6169
|
+
const edgesAdded = to.links.filter((e) => !fromEdgeKeys.has(edgeKey(e)));
|
|
6170
|
+
const edgesRemoved = from.links.filter((e) => !toEdgeKeys.has(edgeKey(e)));
|
|
6171
|
+
return {
|
|
6172
|
+
nodes: { added: nodesAdded, removed: nodesRemoved },
|
|
6173
|
+
edges: { added: edgesAdded, removed: edgesRemoved },
|
|
6174
|
+
counts: {
|
|
6175
|
+
nodes_added: nodesAdded.length,
|
|
6176
|
+
nodes_removed: nodesRemoved.length,
|
|
6177
|
+
edges_added: edgesAdded.length,
|
|
6178
|
+
edges_removed: edgesRemoved.length
|
|
6179
|
+
}
|
|
6180
|
+
};
|
|
6181
|
+
}
|
|
6182
|
+
function loadSnapshotByCommit(baseDir, commitSha) {
|
|
6183
|
+
if (!/^[0-9a-f]{4,64}$/i.test(commitSha))
|
|
6184
|
+
return null;
|
|
6185
|
+
const path = join25(baseDir, "snapshots", `${commitSha}.json`);
|
|
6186
|
+
if (!existsSync19(path))
|
|
6187
|
+
return null;
|
|
6188
|
+
let raw;
|
|
6189
|
+
try {
|
|
6190
|
+
raw = readFileSync18(path, "utf8");
|
|
6191
|
+
} catch {
|
|
6192
|
+
return null;
|
|
6193
|
+
}
|
|
6194
|
+
try {
|
|
6195
|
+
const parsed = JSON.parse(raw);
|
|
6196
|
+
if (!isGraphSnapshotLike(parsed))
|
|
6197
|
+
return null;
|
|
6198
|
+
return parsed;
|
|
6199
|
+
} catch {
|
|
6200
|
+
return null;
|
|
6201
|
+
}
|
|
6202
|
+
}
|
|
6203
|
+
function isGraphSnapshotLike(v) {
|
|
6204
|
+
if (v === null || typeof v !== "object")
|
|
6205
|
+
return false;
|
|
6206
|
+
const s = v;
|
|
6207
|
+
return Array.isArray(s.nodes) && Array.isArray(s.links);
|
|
6208
|
+
}
|
|
6209
|
+
function printDiffHuman(diff, sampleSize = 10) {
|
|
6210
|
+
const { counts } = diff;
|
|
6211
|
+
console.log(`Nodes: +${counts.nodes_added} -${counts.nodes_removed} Edges: +${counts.edges_added} -${counts.edges_removed}`);
|
|
6212
|
+
const showNodes = (label, ns) => {
|
|
6213
|
+
if (ns.length === 0)
|
|
6214
|
+
return;
|
|
6215
|
+
console.log("");
|
|
6216
|
+
console.log(`${label} (${ns.length}, showing up to ${sampleSize}):`);
|
|
6217
|
+
const sorted = [...ns].sort((a, b) => a.id < b.id ? -1 : a.id > b.id ? 1 : 0);
|
|
6218
|
+
for (const n of sorted.slice(0, sampleSize)) {
|
|
6219
|
+
console.log(` ${n.id} [${n.kind}]${n.exported ? " (exported)" : ""} ${n.source_file}:${n.source_location}`);
|
|
6220
|
+
}
|
|
6221
|
+
if (sorted.length > sampleSize)
|
|
6222
|
+
console.log(` \u2026 and ${sorted.length - sampleSize} more`);
|
|
6223
|
+
};
|
|
6224
|
+
const showEdges = (label, es) => {
|
|
6225
|
+
if (es.length === 0)
|
|
6226
|
+
return;
|
|
6227
|
+
console.log("");
|
|
6228
|
+
console.log(`${label} (${es.length}, showing up to ${sampleSize}):`);
|
|
6229
|
+
const sorted = [...es].sort((a, b) => edgeKey(a) < edgeKey(b) ? -1 : edgeKey(a) > edgeKey(b) ? 1 : 0);
|
|
6230
|
+
for (const e of sorted.slice(0, sampleSize)) {
|
|
6231
|
+
console.log(` ${e.source} --${e.relation}--> ${e.target}${e.ord !== void 0 ? ` (ord=${e.ord})` : ""}`);
|
|
6232
|
+
}
|
|
6233
|
+
if (sorted.length > sampleSize)
|
|
6234
|
+
console.log(` \u2026 and ${sorted.length - sampleSize} more`);
|
|
6235
|
+
};
|
|
6236
|
+
showNodes("Nodes added", diff.nodes.added);
|
|
6237
|
+
showNodes("Nodes removed", diff.nodes.removed);
|
|
6238
|
+
showEdges("Edges added", diff.edges.added);
|
|
6239
|
+
showEdges("Edges removed", diff.edges.removed);
|
|
6240
|
+
}
|
|
6241
|
+
|
|
6242
|
+
// dist/src/graph/extract/typescript.js
|
|
6243
|
+
import Parser from "tree-sitter";
|
|
6244
|
+
import TypeScript from "tree-sitter-typescript";
|
|
6245
|
+
var _typescriptParser = null;
|
|
6246
|
+
var _tsxParser = null;
|
|
6247
|
+
function getTypescriptParser() {
|
|
6248
|
+
if (_typescriptParser === null) {
|
|
6249
|
+
_typescriptParser = new Parser();
|
|
6250
|
+
_typescriptParser.setLanguage(TypeScript.typescript);
|
|
6251
|
+
}
|
|
6252
|
+
return _typescriptParser;
|
|
6253
|
+
}
|
|
6254
|
+
function getTsxParser() {
|
|
6255
|
+
if (_tsxParser === null) {
|
|
6256
|
+
_tsxParser = new Parser();
|
|
6257
|
+
_tsxParser.setLanguage(TypeScript.tsx);
|
|
6258
|
+
}
|
|
6259
|
+
return _tsxParser;
|
|
6260
|
+
}
|
|
6261
|
+
function pickParserForPath(relativePath) {
|
|
6262
|
+
return relativePath.endsWith(".tsx") || relativePath.endsWith(".jsx") ? getTsxParser() : getTypescriptParser();
|
|
6263
|
+
}
|
|
6264
|
+
function extractTypeScript(sourceCode, relativePath) {
|
|
6265
|
+
const parser = pickParserForPath(relativePath);
|
|
6266
|
+
const CHUNK_BYTES = 16384;
|
|
6267
|
+
const tree = parser.parse((index) => {
|
|
6268
|
+
if (index >= sourceCode.length)
|
|
6269
|
+
return null;
|
|
6270
|
+
return sourceCode.slice(index, index + CHUNK_BYTES);
|
|
6271
|
+
});
|
|
6272
|
+
const root = tree.rootNode;
|
|
6273
|
+
const result = {
|
|
6274
|
+
source_file: relativePath,
|
|
6275
|
+
language: "typescript",
|
|
6276
|
+
nodes: [],
|
|
6277
|
+
edges: [],
|
|
6278
|
+
parse_errors: []
|
|
6279
|
+
};
|
|
6280
|
+
collectParseErrors(root, relativePath, result.parse_errors);
|
|
6281
|
+
const moduleNode = makeModuleNode(relativePath);
|
|
6282
|
+
result.nodes.push(moduleNode);
|
|
6283
|
+
const declByName = /* @__PURE__ */ new Map();
|
|
6284
|
+
extractDeclarations(root, relativePath, result, declByName, moduleNode);
|
|
6285
|
+
extractImports(root, relativePath, result, moduleNode);
|
|
6286
|
+
extractCalls(root, relativePath, result, declByName);
|
|
6287
|
+
return result;
|
|
6288
|
+
}
|
|
6289
|
+
function collectParseErrors(node, relativePath, out) {
|
|
6290
|
+
if (node.isError || node.isMissing) {
|
|
6291
|
+
out.push({
|
|
6292
|
+
source_file: relativePath,
|
|
6293
|
+
message: node.isMissing ? `missing node: ${node.type}` : `parse error at ${locationStr(node)}`,
|
|
6294
|
+
location: locationStr(node)
|
|
6295
|
+
});
|
|
6296
|
+
return;
|
|
6297
|
+
}
|
|
6298
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
6299
|
+
const child = node.namedChild(i);
|
|
6300
|
+
if (child !== null)
|
|
6301
|
+
collectParseErrors(child, relativePath, out);
|
|
6302
|
+
}
|
|
6303
|
+
}
|
|
6304
|
+
function extractDeclarations(node, relativePath, result, declByName, moduleNode) {
|
|
6305
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
6306
|
+
const child = node.namedChild(i);
|
|
6307
|
+
if (child === null)
|
|
6308
|
+
continue;
|
|
6309
|
+
const { decl, exported } = unwrapExport(child);
|
|
6310
|
+
if (decl !== null) {
|
|
6311
|
+
handleDeclaration(decl, exported, relativePath, result, declByName, moduleNode);
|
|
6312
|
+
}
|
|
6313
|
+
if (child.type === "internal_module" || child.type === "module") {
|
|
6314
|
+
extractDeclarations(child, relativePath, result, declByName, moduleNode);
|
|
6315
|
+
}
|
|
6316
|
+
}
|
|
6317
|
+
}
|
|
6318
|
+
function unwrapExport(node) {
|
|
6319
|
+
if (node.type === "export_statement") {
|
|
6320
|
+
const decl = node.childForFieldName("declaration") ?? firstNamedChildOfTypes(node, [
|
|
6321
|
+
"function_declaration",
|
|
6322
|
+
"class_declaration",
|
|
6323
|
+
"interface_declaration",
|
|
6324
|
+
"type_alias_declaration",
|
|
6325
|
+
"enum_declaration",
|
|
6326
|
+
"lexical_declaration"
|
|
6327
|
+
]);
|
|
6328
|
+
return { decl, exported: true };
|
|
6329
|
+
}
|
|
6330
|
+
return { decl: node, exported: false };
|
|
6331
|
+
}
|
|
6332
|
+
function handleDeclaration(node, exported, relativePath, result, declByName, moduleNode) {
|
|
6333
|
+
switch (node.type) {
|
|
6334
|
+
case "function_declaration": {
|
|
6335
|
+
const name = textOfField(node, "name");
|
|
6336
|
+
if (name === null)
|
|
6337
|
+
return;
|
|
6338
|
+
const decl = makeNode(relativePath, name, "function", node, exported);
|
|
6339
|
+
pushNode(result, declByName, decl);
|
|
6340
|
+
return;
|
|
6341
|
+
}
|
|
6342
|
+
case "class_declaration": {
|
|
6343
|
+
const name = textOfField(node, "name");
|
|
6344
|
+
if (name === null)
|
|
6345
|
+
return;
|
|
6346
|
+
const classNode = makeNode(relativePath, name, "class", node, exported);
|
|
6347
|
+
pushNode(result, declByName, classNode);
|
|
6348
|
+
const heritage = firstNamedChildOfTypes(node, ["class_heritage"]);
|
|
6349
|
+
if (heritage !== null) {
|
|
6350
|
+
for (let i = 0; i < heritage.namedChildCount; i++) {
|
|
6351
|
+
const clause = heritage.namedChild(i);
|
|
6352
|
+
if (clause === null)
|
|
6353
|
+
continue;
|
|
6354
|
+
const relation = clause.type === "extends_clause" ? "extends" : clause.type === "implements_clause" ? "implements" : null;
|
|
6355
|
+
if (relation === null)
|
|
6356
|
+
continue;
|
|
6357
|
+
for (let j = 0; j < clause.namedChildCount; j++) {
|
|
6358
|
+
const base = clause.namedChild(j);
|
|
6359
|
+
if (base === null)
|
|
6360
|
+
continue;
|
|
6361
|
+
const baseName = base.text;
|
|
6362
|
+
if (baseName.length === 0)
|
|
6363
|
+
continue;
|
|
6364
|
+
result.edges.push({
|
|
6365
|
+
source: classNode.id,
|
|
6366
|
+
target: nodeIdUnresolved(relativePath, baseName, relation === "extends" ? "class" : "interface"),
|
|
6367
|
+
relation,
|
|
6368
|
+
confidence: "EXTRACTED"
|
|
6369
|
+
});
|
|
6370
|
+
}
|
|
6371
|
+
}
|
|
6372
|
+
}
|
|
6373
|
+
const body = firstNamedChildOfTypes(node, ["class_body"]);
|
|
6374
|
+
if (body !== null) {
|
|
6375
|
+
for (let i = 0; i < body.namedChildCount; i++) {
|
|
6376
|
+
const member = body.namedChild(i);
|
|
6377
|
+
if (member === null)
|
|
6378
|
+
continue;
|
|
6379
|
+
if (member.type === "method_definition") {
|
|
6380
|
+
const methodName = textOfField(member, "name");
|
|
6381
|
+
if (methodName === null)
|
|
6382
|
+
continue;
|
|
6383
|
+
const accessibility = firstNamedChildOfTypes(member, ["accessibility_modifier"]);
|
|
6384
|
+
const isHardPrivate = firstNamedChildOfTypes(member, ["private_property_identifier"]) !== null;
|
|
6385
|
+
const isPublic = !isHardPrivate && (accessibility === null || accessibility.text === "public");
|
|
6386
|
+
const methodExported = exported && isPublic;
|
|
6387
|
+
const methodKey = `${classNode.label}.${methodName}`;
|
|
6388
|
+
const methodNode = makeNodeWithExplicitLabel(relativePath, methodKey, methodName, "method", member, methodExported);
|
|
6389
|
+
pushNode(result, declByName, methodNode, methodKey);
|
|
6390
|
+
result.edges.push({
|
|
6391
|
+
source: classNode.id,
|
|
6392
|
+
target: methodNode.id,
|
|
6393
|
+
relation: "method_of",
|
|
6394
|
+
confidence: "EXTRACTED"
|
|
6395
|
+
});
|
|
6396
|
+
}
|
|
6397
|
+
}
|
|
6398
|
+
}
|
|
6399
|
+
return;
|
|
6400
|
+
}
|
|
6401
|
+
case "interface_declaration": {
|
|
6402
|
+
const name = textOfField(node, "name");
|
|
6403
|
+
if (name === null)
|
|
6404
|
+
return;
|
|
6405
|
+
const decl = makeNode(relativePath, name, "interface", node, exported);
|
|
6406
|
+
pushNode(result, declByName, decl);
|
|
6407
|
+
return;
|
|
6408
|
+
}
|
|
6409
|
+
case "type_alias_declaration": {
|
|
6410
|
+
const name = textOfField(node, "name");
|
|
6411
|
+
if (name === null)
|
|
6412
|
+
return;
|
|
6413
|
+
const decl = makeNode(relativePath, name, "type_alias", node, exported);
|
|
6414
|
+
pushNode(result, declByName, decl);
|
|
6415
|
+
return;
|
|
6416
|
+
}
|
|
6417
|
+
case "enum_declaration": {
|
|
6418
|
+
const name = textOfField(node, "name");
|
|
6419
|
+
if (name === null)
|
|
6420
|
+
return;
|
|
6421
|
+
const decl = makeNode(relativePath, name, "enum", node, exported);
|
|
6422
|
+
pushNode(result, declByName, decl);
|
|
6423
|
+
return;
|
|
6424
|
+
}
|
|
6425
|
+
case "lexical_declaration": {
|
|
6426
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
6427
|
+
const declarator = node.namedChild(i);
|
|
6428
|
+
if (declarator === null || declarator.type !== "variable_declarator")
|
|
6429
|
+
continue;
|
|
6430
|
+
const ident = declarator.childForFieldName("name");
|
|
6431
|
+
if (ident === null || ident.type !== "identifier")
|
|
6432
|
+
continue;
|
|
6433
|
+
const decl = makeNode(relativePath, ident.text, "const", declarator, exported);
|
|
6434
|
+
pushNode(result, declByName, decl);
|
|
6435
|
+
}
|
|
6436
|
+
return;
|
|
6437
|
+
}
|
|
6438
|
+
}
|
|
6439
|
+
}
|
|
6440
|
+
function extractImports(node, relativePath, result, moduleNode) {
|
|
6441
|
+
if (node.type === "import_statement") {
|
|
6442
|
+
const src = firstNamedChildOfTypes(node, ["string"]);
|
|
6443
|
+
if (src !== null) {
|
|
6444
|
+
const frag = firstNamedChildOfTypes(src, ["string_fragment"]);
|
|
6445
|
+
const specifier = (frag !== null ? frag.text : src.text).replace(/^['"]|['"]$/g, "");
|
|
6446
|
+
if (specifier.length > 0) {
|
|
6447
|
+
result.edges.push({
|
|
6448
|
+
source: moduleNode.id,
|
|
6449
|
+
target: `external:${specifier}`,
|
|
6450
|
+
relation: "imports",
|
|
6451
|
+
confidence: "EXTRACTED"
|
|
6452
|
+
});
|
|
6453
|
+
}
|
|
6454
|
+
}
|
|
6455
|
+
return;
|
|
6456
|
+
}
|
|
6457
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
6458
|
+
const child = node.namedChild(i);
|
|
6459
|
+
if (child !== null)
|
|
6460
|
+
extractImports(child, relativePath, result, moduleNode);
|
|
6461
|
+
}
|
|
6462
|
+
}
|
|
6463
|
+
function extractCalls(node, relativePath, result, declByName) {
|
|
6464
|
+
if (node.type === "call_expression") {
|
|
6465
|
+
const callee = node.childForFieldName("function");
|
|
6466
|
+
if (callee !== null) {
|
|
6467
|
+
const calleeKey = resolveCalleeKey(callee, declByName);
|
|
6468
|
+
if (calleeKey !== null) {
|
|
6469
|
+
const targetNode = declByName.get(calleeKey);
|
|
6470
|
+
if (targetNode !== void 0) {
|
|
6471
|
+
const callerNode = findEnclosingDeclaration(node, declByName);
|
|
6472
|
+
if (callerNode !== null) {
|
|
6473
|
+
result.edges.push({
|
|
6474
|
+
source: callerNode.id,
|
|
6475
|
+
target: targetNode.id,
|
|
6476
|
+
relation: "calls",
|
|
6477
|
+
confidence: "EXTRACTED"
|
|
6478
|
+
});
|
|
6479
|
+
}
|
|
6480
|
+
}
|
|
6481
|
+
}
|
|
6482
|
+
}
|
|
6483
|
+
}
|
|
6484
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
6485
|
+
const child = node.namedChild(i);
|
|
6486
|
+
if (child !== null)
|
|
6487
|
+
extractCalls(child, relativePath, result, declByName);
|
|
6488
|
+
}
|
|
6489
|
+
}
|
|
6490
|
+
function resolveCalleeKey(callee, declByName) {
|
|
6491
|
+
if (callee.type === "identifier")
|
|
6492
|
+
return callee.text;
|
|
6493
|
+
if (callee.type === "member_expression") {
|
|
6494
|
+
const object = callee.childForFieldName("object");
|
|
6495
|
+
const property = callee.childForFieldName("property");
|
|
6496
|
+
if (object !== null && object.type === "this" && property !== null && property.type === "property_identifier") {
|
|
6497
|
+
const className = findEnclosingClassName(callee);
|
|
6498
|
+
if (className !== null)
|
|
6499
|
+
return `${className}.${property.text}`;
|
|
6500
|
+
}
|
|
6501
|
+
}
|
|
6502
|
+
return null;
|
|
6503
|
+
}
|
|
6504
|
+
function findEnclosingDeclaration(node, declByName) {
|
|
6505
|
+
let cur = node.parent;
|
|
6506
|
+
while (cur !== null) {
|
|
6507
|
+
if (cur.type === "function_declaration") {
|
|
6508
|
+
const name = textOfField(cur, "name");
|
|
6509
|
+
if (name !== null) {
|
|
6510
|
+
const n = declByName.get(name);
|
|
6511
|
+
if (n !== void 0)
|
|
6512
|
+
return n;
|
|
6513
|
+
}
|
|
6514
|
+
} else if (cur.type === "method_definition") {
|
|
6515
|
+
const methodName = textOfField(cur, "name");
|
|
6516
|
+
const className = findEnclosingClassName(cur);
|
|
6517
|
+
if (methodName !== null && className !== null) {
|
|
6518
|
+
const n = declByName.get(`${className}.${methodName}`);
|
|
6519
|
+
if (n !== void 0)
|
|
6520
|
+
return n;
|
|
6521
|
+
}
|
|
6522
|
+
} else if (cur.type === "variable_declarator") {
|
|
6523
|
+
const value = cur.childForFieldName("value");
|
|
6524
|
+
if (value?.type === "arrow_function" || value?.type === "function_expression") {
|
|
6525
|
+
const ident = cur.childForFieldName("name");
|
|
6526
|
+
if (ident !== null && ident.type === "identifier") {
|
|
6527
|
+
const n = declByName.get(ident.text);
|
|
6528
|
+
if (n !== void 0)
|
|
6529
|
+
return n;
|
|
6530
|
+
}
|
|
6531
|
+
}
|
|
6532
|
+
}
|
|
6533
|
+
cur = cur.parent;
|
|
6534
|
+
}
|
|
6535
|
+
return null;
|
|
6536
|
+
}
|
|
6537
|
+
function findEnclosingClassName(node) {
|
|
6538
|
+
let cur = node.parent;
|
|
6539
|
+
while (cur !== null) {
|
|
6540
|
+
if (cur.type === "class_declaration") {
|
|
6541
|
+
return textOfField(cur, "name");
|
|
6542
|
+
}
|
|
6543
|
+
cur = cur.parent;
|
|
6544
|
+
}
|
|
6545
|
+
return null;
|
|
6546
|
+
}
|
|
6547
|
+
function makeModuleNode(relativePath) {
|
|
6548
|
+
return {
|
|
6549
|
+
id: `${relativePath}::module`,
|
|
6550
|
+
label: relativePath,
|
|
6551
|
+
kind: "module",
|
|
6552
|
+
source_file: relativePath,
|
|
6553
|
+
source_location: "L1",
|
|
6554
|
+
language: "typescript",
|
|
6555
|
+
exported: false
|
|
6556
|
+
};
|
|
6557
|
+
}
|
|
6558
|
+
function makeNode(relativePath, name, kind, node, exported) {
|
|
6559
|
+
return {
|
|
6560
|
+
id: nodeId(relativePath, name, kind),
|
|
6561
|
+
label: name,
|
|
6562
|
+
kind,
|
|
6563
|
+
source_file: relativePath,
|
|
6564
|
+
source_location: locationStr(node),
|
|
6565
|
+
language: "typescript",
|
|
6566
|
+
exported
|
|
6567
|
+
};
|
|
6568
|
+
}
|
|
6569
|
+
function makeNodeWithExplicitLabel(relativePath, idName, label, kind, node, exported) {
|
|
6570
|
+
return {
|
|
6571
|
+
id: nodeId(relativePath, idName, kind),
|
|
6572
|
+
label,
|
|
6573
|
+
kind,
|
|
6574
|
+
source_file: relativePath,
|
|
6575
|
+
source_location: locationStr(node),
|
|
6576
|
+
language: "typescript",
|
|
6577
|
+
exported
|
|
6578
|
+
};
|
|
6579
|
+
}
|
|
6580
|
+
function pushNode(result, declByName, node, lookupKey) {
|
|
6581
|
+
if (result.nodes.some((n) => n.id === node.id)) {
|
|
6582
|
+
if (!declByName.has(lookupKey ?? node.label)) {
|
|
6583
|
+
declByName.set(lookupKey ?? node.label, node);
|
|
6584
|
+
}
|
|
6585
|
+
return;
|
|
6586
|
+
}
|
|
6587
|
+
result.nodes.push(node);
|
|
6588
|
+
declByName.set(lookupKey ?? node.label, node);
|
|
6589
|
+
}
|
|
6590
|
+
function nodeId(relativePath, name, kind) {
|
|
6591
|
+
return `${relativePath}:${name}:${kind}`;
|
|
6592
|
+
}
|
|
6593
|
+
function nodeIdUnresolved(relativePath, name, kind) {
|
|
6594
|
+
return `unresolved:${relativePath}:${name}:${kind}`;
|
|
6595
|
+
}
|
|
6596
|
+
function locationStr(node) {
|
|
6597
|
+
const start = node.startPosition.row + 1;
|
|
6598
|
+
const end = node.endPosition.row + 1;
|
|
6599
|
+
return start === end ? `L${start}` : `L${start}-${end}`;
|
|
6600
|
+
}
|
|
6601
|
+
function textOfField(node, fieldName) {
|
|
6602
|
+
const child = node.childForFieldName(fieldName);
|
|
6603
|
+
if (child === null)
|
|
6604
|
+
return null;
|
|
6605
|
+
const text = child.text;
|
|
6606
|
+
return text.length > 0 ? text : null;
|
|
6607
|
+
}
|
|
6608
|
+
function firstNamedChildOfTypes(node, types) {
|
|
6609
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
6610
|
+
const child = node.namedChild(i);
|
|
6611
|
+
if (child !== null && types.includes(child.type))
|
|
6612
|
+
return child;
|
|
6613
|
+
}
|
|
6614
|
+
return null;
|
|
6615
|
+
}
|
|
6616
|
+
|
|
6617
|
+
// dist/src/graph/git-hook-install.js
|
|
6618
|
+
import { chmodSync as chmodSync2, existsSync as existsSync20, mkdirSync as mkdirSync12, readFileSync as readFileSync19, unlinkSync as unlinkSync8, writeFileSync as writeFileSync15 } from "node:fs";
|
|
6619
|
+
import { dirname as dirname8, join as join26, resolve as resolve3 } from "node:path";
|
|
6620
|
+
import { execFileSync as execFileSync5 } from "node:child_process";
|
|
6621
|
+
var HOOK_BEGIN_MARKER = "# HIVEMIND_GRAPH_HOOK_BEGIN \u2014 managed by `hivemind graph init`";
|
|
6622
|
+
var HOOK_END_MARKER = "# HIVEMIND_GRAPH_HOOK_END";
|
|
6623
|
+
var SHEBANG = "#!/bin/sh";
|
|
6624
|
+
function hookBodyLines(hivemindPath) {
|
|
6625
|
+
return [
|
|
6626
|
+
"# Async-detached so commits never wait. Threshold-gate + cache make",
|
|
6627
|
+
"# typical re-runs ~85ms. Logs go to ~/.hivemind/post-commit.log",
|
|
6628
|
+
"# mkdir is robust against first-run: $HOME/.hivemind may not exist yet,",
|
|
6629
|
+
"# in which case the > redirect would fail and the build would never start.",
|
|
6630
|
+
'mkdir -p "$HOME/.hivemind" 2>/dev/null || true',
|
|
6631
|
+
`nohup ${quoteForShell(hivemindPath)} graph build --trigger post-commit >> "$HOME/.hivemind/post-commit.log" 2>&1 &`
|
|
6632
|
+
];
|
|
6633
|
+
}
|
|
6634
|
+
function quoteForShell(path) {
|
|
6635
|
+
return `'${path.replace(/'/g, `'\\''`)}'`;
|
|
6636
|
+
}
|
|
6637
|
+
function gitHooksDir(cwd) {
|
|
6638
|
+
const configured = tryGitConfig(cwd, "core.hooksPath");
|
|
6639
|
+
if (configured !== null) {
|
|
6640
|
+
const top = tryGitTopLevel(cwd);
|
|
6641
|
+
return top !== null ? resolve3(top, configured) : resolve3(cwd, configured);
|
|
6642
|
+
}
|
|
6643
|
+
try {
|
|
6644
|
+
const out = execFileSync5("git", ["rev-parse", "--git-path", "hooks"], {
|
|
6645
|
+
cwd,
|
|
6646
|
+
encoding: "utf8",
|
|
6647
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
6648
|
+
}).trim();
|
|
6649
|
+
if (out === "")
|
|
6650
|
+
return null;
|
|
6651
|
+
return resolve3(cwd, out);
|
|
6652
|
+
} catch {
|
|
6653
|
+
return null;
|
|
6654
|
+
}
|
|
6655
|
+
}
|
|
6656
|
+
function tryGitConfig(cwd, key) {
|
|
6657
|
+
try {
|
|
6658
|
+
const out = execFileSync5("git", ["config", "--get", key], {
|
|
6659
|
+
cwd,
|
|
6660
|
+
encoding: "utf8",
|
|
6661
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
6662
|
+
}).trim();
|
|
6663
|
+
return out === "" ? null : out;
|
|
6664
|
+
} catch {
|
|
6665
|
+
return null;
|
|
6666
|
+
}
|
|
6667
|
+
}
|
|
6668
|
+
function tryGitTopLevel(cwd) {
|
|
6669
|
+
try {
|
|
6670
|
+
const out = execFileSync5("git", ["rev-parse", "--show-toplevel"], {
|
|
6671
|
+
cwd,
|
|
6672
|
+
encoding: "utf8",
|
|
6673
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
6674
|
+
}).trim();
|
|
6675
|
+
return out === "" ? null : out;
|
|
6676
|
+
} catch {
|
|
6677
|
+
return null;
|
|
6678
|
+
}
|
|
6679
|
+
}
|
|
6680
|
+
function postCommitHookPath(cwd) {
|
|
6681
|
+
const hooksDir = gitHooksDir(cwd);
|
|
6682
|
+
return hooksDir === null ? null : join26(hooksDir, "post-commit");
|
|
6683
|
+
}
|
|
6684
|
+
function installPostCommitHook(cwd, opts = {}) {
|
|
6685
|
+
const path = postCommitHookPath(cwd);
|
|
6686
|
+
if (path === null) {
|
|
6687
|
+
return { kind: "foreign-hook", path: "", hint: "not in a git repo (no .git directory found)" };
|
|
6688
|
+
}
|
|
6689
|
+
const existed = existsSync20(path);
|
|
6690
|
+
if (existed) {
|
|
6691
|
+
const content = readFileSync19(path, "utf8");
|
|
6692
|
+
if (containsOurMarkers(content)) {
|
|
6693
|
+
return { kind: "already-ours", path };
|
|
6694
|
+
}
|
|
6695
|
+
if (!opts.force) {
|
|
6696
|
+
return {
|
|
6697
|
+
kind: "foreign-hook",
|
|
6698
|
+
path,
|
|
6699
|
+
hint: `existing hook at ${path} is not managed by hivemind; pass --force to overwrite, or merge our block manually (between '${HOOK_BEGIN_MARKER}' and '${HOOK_END_MARKER}')`
|
|
6700
|
+
};
|
|
6701
|
+
}
|
|
6702
|
+
}
|
|
6703
|
+
const hivemindPath = resolveHivemindPath();
|
|
6704
|
+
if (hivemindPath === null) {
|
|
6705
|
+
return {
|
|
6706
|
+
kind: "foreign-hook",
|
|
6707
|
+
path,
|
|
6708
|
+
hint: "hivemind binary not found on PATH. Install hivemind globally (`npm install -g @deeplake/hivemind`) before running `hivemind graph init`, so the hook can find a stable absolute path to call."
|
|
6709
|
+
};
|
|
6710
|
+
}
|
|
6711
|
+
mkdirSync12(dirname8(path), { recursive: true });
|
|
6712
|
+
writeFileSync15(path, buildHookFile(hivemindPath), { mode: 493 });
|
|
6713
|
+
try {
|
|
6714
|
+
chmodSync2(path, 493);
|
|
6715
|
+
} catch {
|
|
6716
|
+
}
|
|
6717
|
+
return { kind: "installed", path, wasNew: !existed };
|
|
6718
|
+
}
|
|
6719
|
+
function resolveHivemindPath() {
|
|
6720
|
+
try {
|
|
6721
|
+
const out = execFileSync5("which", ["hivemind"], {
|
|
6722
|
+
encoding: "utf8",
|
|
6723
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
6724
|
+
}).trim();
|
|
6725
|
+
if (out !== "" && out.includes("hivemind"))
|
|
6726
|
+
return out.split("\n")[0].trim();
|
|
6727
|
+
} catch {
|
|
6728
|
+
}
|
|
6729
|
+
return null;
|
|
6730
|
+
}
|
|
6731
|
+
function uninstallPostCommitHook(cwd) {
|
|
6732
|
+
const path = postCommitHookPath(cwd);
|
|
6733
|
+
if (path === null) {
|
|
6734
|
+
return { kind: "no-hook", path: "" };
|
|
6735
|
+
}
|
|
6736
|
+
if (!existsSync20(path)) {
|
|
6737
|
+
return { kind: "no-hook", path };
|
|
6738
|
+
}
|
|
6739
|
+
const content = readFileSync19(path, "utf8");
|
|
6740
|
+
if (!containsOurMarkers(content)) {
|
|
6741
|
+
return {
|
|
6742
|
+
kind: "not-ours",
|
|
6743
|
+
path,
|
|
6744
|
+
hint: `existing hook at ${path} is not managed by hivemind; remove it manually if you want it gone`
|
|
6745
|
+
};
|
|
6746
|
+
}
|
|
6747
|
+
const stripped = stripOurBlock(content);
|
|
6748
|
+
const meaningful = stripped.split("\n").map((l) => l.trim()).filter((l) => l.length > 0 && !l.startsWith("#!"));
|
|
6749
|
+
if (meaningful.length === 0) {
|
|
6750
|
+
unlinkSync8(path);
|
|
6751
|
+
return { kind: "removed", path, wholeFileDeleted: true };
|
|
6752
|
+
}
|
|
6753
|
+
writeFileSync15(path, stripped);
|
|
6754
|
+
return { kind: "removed", path, wholeFileDeleted: false };
|
|
6755
|
+
}
|
|
6756
|
+
function containsOurMarkers(content) {
|
|
6757
|
+
return content.includes(HOOK_BEGIN_MARKER) && content.includes(HOOK_END_MARKER);
|
|
6758
|
+
}
|
|
6759
|
+
function stripOurBlock(content) {
|
|
6760
|
+
const beginIdx = content.indexOf(HOOK_BEGIN_MARKER);
|
|
6761
|
+
const endIdx = content.indexOf(HOOK_END_MARKER);
|
|
6762
|
+
if (beginIdx === -1 || endIdx === -1 || endIdx < beginIdx)
|
|
6763
|
+
return content;
|
|
6764
|
+
const blockEnd = endIdx + HOOK_END_MARKER.length;
|
|
6765
|
+
return content.slice(0, beginIdx) + content.slice(blockEnd);
|
|
6766
|
+
}
|
|
6767
|
+
function buildHookFile(hivemindPath) {
|
|
6768
|
+
return [
|
|
6769
|
+
SHEBANG,
|
|
6770
|
+
"",
|
|
6771
|
+
HOOK_BEGIN_MARKER,
|
|
6772
|
+
...hookBodyLines(hivemindPath),
|
|
6773
|
+
HOOK_END_MARKER,
|
|
6774
|
+
""
|
|
6775
|
+
].join("\n");
|
|
6776
|
+
}
|
|
6777
|
+
|
|
6778
|
+
// dist/src/commands/graph.js
|
|
6779
|
+
var USAGE = `hivemind graph \u2014 codebase-graph commands (Phase 1 \u2014 TypeScript only)
|
|
6780
|
+
|
|
6781
|
+
Usage:
|
|
6782
|
+
hivemind graph build [--cwd <path>]
|
|
6783
|
+
Walk the project for TypeScript source files, extract symbols + edges,
|
|
6784
|
+
and write a snapshot to ~/.hivemind/graphs/<repo-key>/snapshots/<commit-sha>.json.
|
|
6785
|
+
Also updates ~/.hivemind/graphs/<repo-key>/latest-commit.txt and the
|
|
6786
|
+
per-repo .last-build.json (consumed by the SessionEnd auto-build hook).
|
|
6787
|
+
|
|
6788
|
+
hivemind graph diff <sha1> <sha2> [--cwd <path>] [--json] [--limit N]
|
|
6789
|
+
Diff two snapshots by their git commit SHA. Prints added/removed
|
|
6790
|
+
counts for nodes and edges, plus up to N=10 (default) examples of each.
|
|
6791
|
+
--json: emit machine-readable JSON instead of the human format.
|
|
6792
|
+
--limit N: cap the per-category examples (human format only).
|
|
6793
|
+
|
|
6794
|
+
hivemind graph history [--cwd <path>] [-n N] [--json]
|
|
6795
|
+
Print the last N (default 20) entries from the per-repo history.jsonl,
|
|
6796
|
+
newest last. Each entry shows ts, commit_sha (short), snapshot_sha256
|
|
6797
|
+
(short), node/edge counts, and the trigger that fired the build.
|
|
6798
|
+
--json: emit raw JSONL (one parsed entry per line, full fields).
|
|
6799
|
+
|
|
6800
|
+
hivemind graph init [--cwd <path>] [--force] [--no-initial-build]
|
|
6801
|
+
Install a managed block in .git/hooks/post-commit that fires
|
|
6802
|
+
\`hivemind graph build --trigger post-commit\` after each commit
|
|
6803
|
+
(async, non-blocking, exit 0 always). Idempotent: re-running on
|
|
6804
|
+
an already-installed hook is a no-op. Refuses to clobber an
|
|
6805
|
+
existing non-managed hook unless --force is passed.
|
|
6806
|
+
Also runs an initial \`hivemind graph build\` unless
|
|
6807
|
+
--no-initial-build is passed.
|
|
6808
|
+
|
|
6809
|
+
hivemind graph uninstall [--cwd <path>]
|
|
6810
|
+
Remove our managed block from .git/hooks/post-commit. If our block
|
|
6811
|
+
was the only content, deletes the file; otherwise leaves the rest
|
|
6812
|
+
intact. Snapshots and history are NOT touched (\`rm -rf
|
|
6813
|
+
~/.hivemind/graphs/<key>\` if you really want them gone).
|
|
6814
|
+
|
|
6815
|
+
hivemind graph pull [--cwd <path>]
|
|
6816
|
+
Download the freshest cloud snapshot for HEAD into the local graph
|
|
6817
|
+
dir (any worktree of this user counts). No-op if local already
|
|
6818
|
+
matches cloud sha256 or local was built later than cloud. Requires
|
|
6819
|
+
\`hivemind login\`. Best-effort: any network/auth failure leaves
|
|
6820
|
+
the local files untouched. Disable via HIVEMIND_GRAPH_PULL=0.
|
|
6821
|
+
|
|
6822
|
+
hivemind graph --help
|
|
6823
|
+
Show this message.
|
|
6824
|
+
|
|
6825
|
+
Future subcommands (Phase 1.5+): daemon, search, latest, push, pull, prune.
|
|
6826
|
+
`;
|
|
6827
|
+
var DEFAULT_IGNORES = /* @__PURE__ */ new Set([
|
|
6828
|
+
"node_modules",
|
|
6829
|
+
".git",
|
|
6830
|
+
"bundle",
|
|
6831
|
+
"dist",
|
|
6832
|
+
"coverage",
|
|
6833
|
+
".cache",
|
|
6834
|
+
".nyc_output"
|
|
6835
|
+
]);
|
|
6836
|
+
function runGraphCommand(args) {
|
|
6837
|
+
const sub = args[0];
|
|
6838
|
+
if (sub === void 0 || sub === "--help" || sub === "-h" || sub === "help") {
|
|
6839
|
+
console.log(USAGE);
|
|
6840
|
+
return;
|
|
6841
|
+
}
|
|
6842
|
+
if (sub === "build") {
|
|
6843
|
+
return runBuildCommand(args.slice(1));
|
|
6844
|
+
}
|
|
6845
|
+
if (sub === "diff") {
|
|
6846
|
+
runDiffCommand(args.slice(1));
|
|
6847
|
+
return;
|
|
6848
|
+
}
|
|
6849
|
+
if (sub === "history") {
|
|
6850
|
+
runHistoryCommand(args.slice(1));
|
|
6851
|
+
return;
|
|
6852
|
+
}
|
|
6853
|
+
if (sub === "init") {
|
|
6854
|
+
return runInitCommand(args.slice(1));
|
|
6855
|
+
}
|
|
6856
|
+
if (sub === "uninstall") {
|
|
6857
|
+
runUninstallCommand(args.slice(1));
|
|
6858
|
+
return;
|
|
6859
|
+
}
|
|
6860
|
+
if (sub === "pull") {
|
|
6861
|
+
return runPullCommand(args.slice(1));
|
|
6862
|
+
}
|
|
6863
|
+
console.error(`hivemind graph: unknown subcommand '${sub}'`);
|
|
6864
|
+
console.error(USAGE);
|
|
6865
|
+
process.exit(2);
|
|
6866
|
+
}
|
|
6867
|
+
function parseInitArgs(args) {
|
|
6868
|
+
let cwd = process.cwd();
|
|
6869
|
+
let force = false;
|
|
6870
|
+
let initialBuild = true;
|
|
6871
|
+
for (let i = 0; i < args.length; i++) {
|
|
6872
|
+
const a = args[i];
|
|
6873
|
+
if (a === "--cwd" && i + 1 < args.length) {
|
|
6874
|
+
cwd = args[i + 1];
|
|
6875
|
+
i += 1;
|
|
6876
|
+
} else if (a === "--force") {
|
|
6877
|
+
force = true;
|
|
6878
|
+
} else if (a === "--no-initial-build") {
|
|
6879
|
+
initialBuild = false;
|
|
6880
|
+
} else if (a === "--help" || a === "-h") {
|
|
6881
|
+
console.log(USAGE);
|
|
6882
|
+
process.exit(0);
|
|
6883
|
+
} else {
|
|
6884
|
+
console.error(`hivemind graph init: unknown argument '${a}'`);
|
|
6885
|
+
console.error(USAGE);
|
|
6886
|
+
process.exit(2);
|
|
6887
|
+
}
|
|
6888
|
+
}
|
|
6889
|
+
return { cwd, force, initialBuild };
|
|
6890
|
+
}
|
|
6891
|
+
async function runInitCommand(args) {
|
|
6892
|
+
const opts = parseInitArgs(args);
|
|
6893
|
+
const status = installPostCommitHook(opts.cwd, { force: opts.force });
|
|
6894
|
+
switch (status.kind) {
|
|
6895
|
+
case "installed":
|
|
6896
|
+
console.log(`Installed post-commit hook at ${status.path}`);
|
|
5357
6897
|
break;
|
|
5358
|
-
|
|
5359
|
-
|
|
5360
|
-
if (!creds) {
|
|
5361
|
-
console.log("Not logged in.");
|
|
5362
|
-
process.exit(1);
|
|
5363
|
-
}
|
|
5364
|
-
const userId = args[1];
|
|
5365
|
-
if (!userId) {
|
|
5366
|
-
console.log("Usage: remove <user-id>");
|
|
5367
|
-
process.exit(1);
|
|
5368
|
-
}
|
|
5369
|
-
await removeMember(userId, creds.token, creds.orgId, apiUrl);
|
|
5370
|
-
console.log(`Removed user ${userId}`);
|
|
6898
|
+
case "already-ours":
|
|
6899
|
+
console.log(`Post-commit hook already managed by hivemind (no change): ${status.path}`);
|
|
5371
6900
|
break;
|
|
6901
|
+
case "foreign-hook":
|
|
6902
|
+
console.error(`hivemind graph init: ${status.hint}`);
|
|
6903
|
+
process.exit(1);
|
|
6904
|
+
}
|
|
6905
|
+
if (opts.initialBuild) {
|
|
6906
|
+
console.log("");
|
|
6907
|
+
console.log("Running initial build...");
|
|
6908
|
+
await runBuildCommand(["--cwd", opts.cwd, "--trigger", "manual"]);
|
|
6909
|
+
} else {
|
|
6910
|
+
console.log("");
|
|
6911
|
+
console.log("Skipped initial build (--no-initial-build). Run `hivemind graph build` when ready.");
|
|
6912
|
+
}
|
|
6913
|
+
}
|
|
6914
|
+
function parseUninstallArgs(args) {
|
|
6915
|
+
let cwd = process.cwd();
|
|
6916
|
+
for (let i = 0; i < args.length; i++) {
|
|
6917
|
+
const a = args[i];
|
|
6918
|
+
if (a === "--cwd" && i + 1 < args.length) {
|
|
6919
|
+
cwd = args[i + 1];
|
|
6920
|
+
i += 1;
|
|
6921
|
+
} else if (a === "--help" || a === "-h") {
|
|
6922
|
+
console.log(USAGE);
|
|
6923
|
+
process.exit(0);
|
|
6924
|
+
} else {
|
|
6925
|
+
console.error(`hivemind graph uninstall: unknown argument '${a}'`);
|
|
6926
|
+
console.error(USAGE);
|
|
6927
|
+
process.exit(2);
|
|
5372
6928
|
}
|
|
5373
|
-
|
|
5374
|
-
|
|
5375
|
-
|
|
5376
|
-
|
|
6929
|
+
}
|
|
6930
|
+
return { cwd };
|
|
6931
|
+
}
|
|
6932
|
+
function runUninstallCommand(args) {
|
|
6933
|
+
const opts = parseUninstallArgs(args);
|
|
6934
|
+
const status = uninstallPostCommitHook(opts.cwd);
|
|
6935
|
+
switch (status.kind) {
|
|
6936
|
+
case "removed":
|
|
6937
|
+
if (status.wholeFileDeleted) {
|
|
6938
|
+
console.log(`Removed post-commit hook (file deleted): ${status.path}`);
|
|
5377
6939
|
} else {
|
|
5378
|
-
console.log(
|
|
6940
|
+
console.log(`Removed managed block from post-commit hook (other content preserved): ${status.path}`);
|
|
5379
6941
|
}
|
|
6942
|
+
console.log("Local snapshots + history.jsonl are untouched.");
|
|
5380
6943
|
break;
|
|
5381
|
-
|
|
5382
|
-
|
|
5383
|
-
|
|
5384
|
-
|
|
5385
|
-
|
|
6944
|
+
case "no-hook":
|
|
6945
|
+
console.log(status.path === "" ? "No git repo here (nothing to uninstall)." : `No post-commit hook at ${status.path} (nothing to uninstall).`);
|
|
6946
|
+
break;
|
|
6947
|
+
case "not-ours":
|
|
6948
|
+
console.error(`hivemind graph uninstall: ${status.hint}`);
|
|
6949
|
+
process.exit(1);
|
|
6950
|
+
}
|
|
6951
|
+
}
|
|
6952
|
+
function parseHistoryArgs(args) {
|
|
6953
|
+
let cwd = process.cwd();
|
|
6954
|
+
let n = 20;
|
|
6955
|
+
let json2 = false;
|
|
6956
|
+
for (let i = 0; i < args.length; i++) {
|
|
6957
|
+
const a = args[i];
|
|
6958
|
+
if (a === "--cwd" && i + 1 < args.length) {
|
|
6959
|
+
cwd = args[i + 1];
|
|
6960
|
+
i += 1;
|
|
6961
|
+
} else if (a === "-n" && i + 1 < args.length) {
|
|
6962
|
+
const raw = args[i + 1];
|
|
6963
|
+
if (!/^\d+$/.test(raw)) {
|
|
6964
|
+
console.error("hivemind graph history: -n must be a non-negative integer");
|
|
6965
|
+
process.exit(2);
|
|
5386
6966
|
}
|
|
5387
|
-
|
|
5388
|
-
|
|
5389
|
-
|
|
5390
|
-
|
|
5391
|
-
|
|
5392
|
-
|
|
5393
|
-
|
|
6967
|
+
n = Number(raw);
|
|
6968
|
+
i += 1;
|
|
6969
|
+
} else if (a === "--json") {
|
|
6970
|
+
json2 = true;
|
|
6971
|
+
} else if (a === "--help" || a === "-h") {
|
|
6972
|
+
console.log(USAGE);
|
|
6973
|
+
process.exit(0);
|
|
6974
|
+
} else {
|
|
6975
|
+
console.error(`hivemind graph history: unknown argument '${a}'`);
|
|
6976
|
+
console.error(USAGE);
|
|
6977
|
+
process.exit(2);
|
|
6978
|
+
}
|
|
6979
|
+
}
|
|
6980
|
+
return { cwd, n, json: json2 };
|
|
6981
|
+
}
|
|
6982
|
+
function runHistoryCommand(args) {
|
|
6983
|
+
const opts = parseHistoryArgs(args);
|
|
6984
|
+
const { key: repoKey } = deriveProjectKey(opts.cwd);
|
|
6985
|
+
const baseDir = repoDir(repoKey);
|
|
6986
|
+
const total = countHistoryEntries(baseDir);
|
|
6987
|
+
const entries = readHistoryTail(baseDir, opts.n);
|
|
6988
|
+
if (opts.json) {
|
|
6989
|
+
for (const e of entries)
|
|
6990
|
+
console.log(JSON.stringify(e));
|
|
6991
|
+
return;
|
|
6992
|
+
}
|
|
6993
|
+
if (total === 0) {
|
|
6994
|
+
console.log("No history yet. Run `hivemind graph build` to record one.");
|
|
6995
|
+
return;
|
|
6996
|
+
}
|
|
6997
|
+
console.log(`history.jsonl: ${total} total entries; showing last ${entries.length}`);
|
|
6998
|
+
console.log("");
|
|
6999
|
+
for (const e of entries) {
|
|
7000
|
+
const commit = e.commit_sha === null ? "(no-git)" : e.commit_sha.slice(0, 7);
|
|
7001
|
+
const snap = e.snapshot_sha256.slice(0, 7);
|
|
7002
|
+
console.log(` ${e.ts} commit=${commit} snap=${snap} nodes=${e.node_count} edges=${e.edge_count} trigger=${e.trigger}`);
|
|
7003
|
+
}
|
|
7004
|
+
}
|
|
7005
|
+
function parseDiffArgs(args) {
|
|
7006
|
+
let cwd = process.cwd();
|
|
7007
|
+
let json2 = false;
|
|
7008
|
+
let limit = 10;
|
|
7009
|
+
const positional = [];
|
|
7010
|
+
for (let i = 0; i < args.length; i++) {
|
|
7011
|
+
const a = args[i];
|
|
7012
|
+
if (a === "--cwd" && i + 1 < args.length) {
|
|
7013
|
+
cwd = args[i + 1];
|
|
7014
|
+
i += 1;
|
|
7015
|
+
} else if (a === "--json") {
|
|
7016
|
+
json2 = true;
|
|
7017
|
+
} else if (a === "--limit" && i + 1 < args.length) {
|
|
7018
|
+
const raw = args[i + 1];
|
|
7019
|
+
if (!/^\d+$/.test(raw)) {
|
|
7020
|
+
console.error("hivemind graph diff: --limit must be a non-negative integer");
|
|
7021
|
+
process.exit(2);
|
|
7022
|
+
}
|
|
7023
|
+
limit = Number(raw);
|
|
7024
|
+
i += 1;
|
|
7025
|
+
} else if (a === "--help" || a === "-h") {
|
|
7026
|
+
console.log(USAGE);
|
|
7027
|
+
process.exit(0);
|
|
7028
|
+
} else if (a !== void 0 && !a.startsWith("--")) {
|
|
7029
|
+
positional.push(a);
|
|
7030
|
+
} else {
|
|
7031
|
+
console.error(`hivemind graph diff: unknown argument '${a}'`);
|
|
7032
|
+
console.error(USAGE);
|
|
7033
|
+
process.exit(2);
|
|
7034
|
+
}
|
|
7035
|
+
}
|
|
7036
|
+
if (positional.length !== 2) {
|
|
7037
|
+
console.error("hivemind graph diff: expected exactly two commit SHAs");
|
|
7038
|
+
console.error(USAGE);
|
|
7039
|
+
process.exit(2);
|
|
7040
|
+
}
|
|
7041
|
+
return { cwd, sha1: positional[0], sha2: positional[1], json: json2, limit };
|
|
7042
|
+
}
|
|
7043
|
+
function runDiffCommand(args) {
|
|
7044
|
+
const opts = parseDiffArgs(args);
|
|
7045
|
+
const { key: repoKey } = deriveProjectKey(opts.cwd);
|
|
7046
|
+
const baseDir = repoDir(repoKey);
|
|
7047
|
+
const from = loadSnapshotByCommit(baseDir, opts.sha1);
|
|
7048
|
+
if (from === null) {
|
|
7049
|
+
console.error(`hivemind graph diff: snapshot not found for ${opts.sha1}`);
|
|
7050
|
+
console.error(` expected: ${baseDir}/snapshots/${opts.sha1}.json`);
|
|
7051
|
+
console.error(" hint: run 'hivemind graph build' on the relevant commit, or check the commit sha");
|
|
7052
|
+
process.exit(1);
|
|
7053
|
+
}
|
|
7054
|
+
const to = loadSnapshotByCommit(baseDir, opts.sha2);
|
|
7055
|
+
if (to === null) {
|
|
7056
|
+
console.error(`hivemind graph diff: snapshot not found for ${opts.sha2}`);
|
|
7057
|
+
console.error(` expected: ${baseDir}/snapshots/${opts.sha2}.json`);
|
|
7058
|
+
process.exit(1);
|
|
7059
|
+
}
|
|
7060
|
+
const diff = diffSnapshots(from, to);
|
|
7061
|
+
if (opts.json) {
|
|
7062
|
+
console.log(JSON.stringify(diff, null, 2));
|
|
7063
|
+
return;
|
|
7064
|
+
}
|
|
7065
|
+
console.log(`Diff: ${opts.sha1} \u2192 ${opts.sha2}`);
|
|
7066
|
+
console.log("");
|
|
7067
|
+
printDiffHuman(diff, opts.limit);
|
|
7068
|
+
}
|
|
7069
|
+
function parseBuildArgs(args) {
|
|
7070
|
+
let cwd = process.cwd();
|
|
7071
|
+
let trigger = "manual";
|
|
7072
|
+
for (let i = 0; i < args.length; i++) {
|
|
7073
|
+
const a = args[i];
|
|
7074
|
+
if (a === "--cwd" && i + 1 < args.length) {
|
|
7075
|
+
cwd = args[i + 1];
|
|
7076
|
+
i += 1;
|
|
7077
|
+
} else if (a === "--trigger" && i + 1 < args.length) {
|
|
7078
|
+
const v = args[i + 1];
|
|
7079
|
+
if (v === "manual" || v === "session-end" || v === "post-commit" || v === "unknown") {
|
|
7080
|
+
trigger = v;
|
|
5394
7081
|
} else {
|
|
5395
|
-
|
|
5396
|
-
|
|
5397
|
-
console.log("Usage: autoupdate [on|off]");
|
|
7082
|
+
console.error(`hivemind graph build: --trigger must be one of manual|session-end|post-commit|unknown (got '${v}')`);
|
|
7083
|
+
process.exit(2);
|
|
5398
7084
|
}
|
|
5399
|
-
|
|
5400
|
-
}
|
|
5401
|
-
|
|
5402
|
-
|
|
5403
|
-
|
|
7085
|
+
i += 1;
|
|
7086
|
+
} else if (a === "--help" || a === "-h") {
|
|
7087
|
+
console.log(USAGE);
|
|
7088
|
+
process.exit(0);
|
|
7089
|
+
} else {
|
|
7090
|
+
console.error(`hivemind graph build: unknown argument '${a}'`);
|
|
7091
|
+
console.error(USAGE);
|
|
7092
|
+
process.exit(2);
|
|
7093
|
+
}
|
|
7094
|
+
}
|
|
7095
|
+
return { cwd, trigger };
|
|
7096
|
+
}
|
|
7097
|
+
async function runBuildCommand(args) {
|
|
7098
|
+
const opts = parseBuildArgs(args);
|
|
7099
|
+
const cwd = resolve4(opts.cwd);
|
|
7100
|
+
const { key: repoKey, project } = deriveProjectKey(cwd);
|
|
7101
|
+
const baseDir = repoDir(repoKey);
|
|
7102
|
+
const commitSha = readGitCommit(cwd);
|
|
7103
|
+
const branch = readGitBranch(cwd);
|
|
7104
|
+
const version = getVersion();
|
|
7105
|
+
console.log(`Building codebase graph for ${project}`);
|
|
7106
|
+
console.log(` repo_key: ${repoKey}`);
|
|
7107
|
+
console.log(` commit_sha: ${commitSha ?? "(not in a git repo)"}`);
|
|
7108
|
+
console.log(` branch: ${branch ?? "(none / detached)"}`);
|
|
7109
|
+
console.log(` output: ${baseDir}`);
|
|
7110
|
+
console.log("");
|
|
7111
|
+
const sourceFiles = discoverSourceFiles(cwd);
|
|
7112
|
+
console.log(`Discovered ${sourceFiles.length} TypeScript source files. Extracting...`);
|
|
7113
|
+
const extractions = [];
|
|
7114
|
+
let skipped = 0;
|
|
7115
|
+
let totalParseErrors = 0;
|
|
7116
|
+
let cacheHits = 0;
|
|
7117
|
+
for (const abs of sourceFiles) {
|
|
7118
|
+
const rel = toForwardSlash(relative(cwd, abs));
|
|
7119
|
+
try {
|
|
7120
|
+
const content = readFileSync20(abs, "utf8");
|
|
7121
|
+
const contentSha = fileContentHash(content);
|
|
7122
|
+
let extraction = readCache(baseDir, contentSha, rel);
|
|
7123
|
+
if (extraction === null) {
|
|
7124
|
+
extraction = extractTypeScript(content, rel);
|
|
7125
|
+
writeCache(baseDir, contentSha, extraction);
|
|
5404
7126
|
} else {
|
|
5405
|
-
|
|
7127
|
+
cacheHits += 1;
|
|
7128
|
+
}
|
|
7129
|
+
if (extraction.parse_errors.length > 0) {
|
|
7130
|
+
totalParseErrors += extraction.parse_errors.length;
|
|
7131
|
+
for (const err of extraction.parse_errors) {
|
|
7132
|
+
console.warn(` warn: parse issue in ${err.source_file} ${err.location ?? ""}: ${err.message}`);
|
|
7133
|
+
}
|
|
5406
7134
|
}
|
|
7135
|
+
extractions.push(extraction);
|
|
7136
|
+
} catch (err) {
|
|
7137
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
7138
|
+
console.warn(` warn: skipping ${rel}: ${msg}`);
|
|
7139
|
+
skipped += 1;
|
|
7140
|
+
}
|
|
7141
|
+
}
|
|
7142
|
+
const metadata = {
|
|
7143
|
+
schema_version: 1,
|
|
7144
|
+
generator: "hivemind-graph",
|
|
7145
|
+
commit_sha: commitSha,
|
|
7146
|
+
repo_key: repoKey
|
|
7147
|
+
};
|
|
7148
|
+
const observation = {
|
|
7149
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7150
|
+
branch,
|
|
7151
|
+
worktree_path: cwd,
|
|
7152
|
+
repo_project: project,
|
|
7153
|
+
generator_version: version,
|
|
7154
|
+
source_files_extracted: extractions.length,
|
|
7155
|
+
source_files_skipped: skipped
|
|
7156
|
+
};
|
|
7157
|
+
const snapshot = buildSnapshot(extractions, metadata, observation);
|
|
7158
|
+
const worktreeId = workTreeIdFor2(cwd);
|
|
7159
|
+
const result = writeSnapshot(snapshot, baseDir, opts.trigger, worktreeId);
|
|
7160
|
+
console.log("");
|
|
7161
|
+
console.log(`Snapshot: ${result.snapshotPath}`);
|
|
7162
|
+
console.log(`Latest: ${result.latestCommitPath ?? "(no commit context \u2014 latest-commit.txt not updated)"}`);
|
|
7163
|
+
console.log(`SHA-256: ${result.snapshotSha256}`);
|
|
7164
|
+
console.log(`Nodes: ${snapshot.nodes.length}`);
|
|
7165
|
+
console.log(`Edges: ${snapshot.links.length}`);
|
|
7166
|
+
console.log(`Files extracted: ${extractions.length} (skipped: ${skipped}, parse warnings: ${totalParseErrors}, cache hits: ${cacheHits}/${sourceFiles.length})`);
|
|
7167
|
+
const pushOutcome = await pushSnapshot(snapshot, worktreeId);
|
|
7168
|
+
switch (pushOutcome.kind) {
|
|
7169
|
+
case "inserted":
|
|
7170
|
+
console.log(`Cloud: pushed to codebase table (commit ${pushOutcome.commitSha.slice(0, 7)})`);
|
|
7171
|
+
break;
|
|
7172
|
+
case "inserted-with-duplicate-race":
|
|
7173
|
+
console.warn(`Cloud: pushed (commit ${pushOutcome.commitSha.slice(0, 7)}) but ${pushOutcome.rowCount} rows now share`);
|
|
7174
|
+
console.warn(` this identity key \u2014 a concurrent writer raced. v1.1 adds a server-side`);
|
|
7175
|
+
console.warn(` UNIQUE constraint; until then, the older row(s) should be deleted manually.`);
|
|
7176
|
+
break;
|
|
7177
|
+
case "already-current":
|
|
7178
|
+
console.log(`Cloud: already up-to-date (commit ${pushOutcome.commitSha.slice(0, 7)})`);
|
|
7179
|
+
break;
|
|
7180
|
+
case "skipped-no-auth":
|
|
7181
|
+
console.log(`Cloud: skipped (not authenticated; run \`hivemind login\` to enable cloud sync)`);
|
|
7182
|
+
break;
|
|
7183
|
+
case "skipped-no-commit":
|
|
7184
|
+
console.log(`Cloud: skipped (no commit context \u2014 not in a git repo)`);
|
|
7185
|
+
break;
|
|
7186
|
+
case "skipped-disabled":
|
|
7187
|
+
console.log(`Cloud: skipped (HIVEMIND_GRAPH_PUSH=0)`);
|
|
7188
|
+
break;
|
|
7189
|
+
case "drift":
|
|
7190
|
+
console.warn(`Cloud: DRIFT \u2014 commit ${pushOutcome.commitSha.slice(0, 7)} is in cloud with`);
|
|
7191
|
+
console.warn(` sha256=${pushOutcome.cloudSha256.slice(0, 12)}... but local rebuild produced`);
|
|
7192
|
+
console.warn(` sha256=${pushOutcome.localSha256.slice(0, 12)}...`);
|
|
7193
|
+
console.warn(` (probably extractor version drift; investigate before forcing.)`);
|
|
7194
|
+
break;
|
|
7195
|
+
case "error":
|
|
7196
|
+
console.warn(`Cloud: push error (non-fatal): ${pushOutcome.message}`);
|
|
7197
|
+
break;
|
|
7198
|
+
}
|
|
7199
|
+
}
|
|
7200
|
+
function parsePullArgs(args) {
|
|
7201
|
+
let cwd = process.cwd();
|
|
7202
|
+
for (let i = 0; i < args.length; i++) {
|
|
7203
|
+
const a = args[i];
|
|
7204
|
+
if (a === "--cwd" && i + 1 < args.length) {
|
|
7205
|
+
cwd = args[i + 1];
|
|
7206
|
+
i += 1;
|
|
7207
|
+
} else if (a === "--help" || a === "-h") {
|
|
7208
|
+
console.log(USAGE);
|
|
7209
|
+
process.exit(0);
|
|
7210
|
+
} else {
|
|
7211
|
+
console.error(`hivemind graph pull: unknown argument '${a}'`);
|
|
7212
|
+
console.error(USAGE);
|
|
7213
|
+
process.exit(2);
|
|
7214
|
+
}
|
|
7215
|
+
}
|
|
7216
|
+
return { cwd };
|
|
7217
|
+
}
|
|
7218
|
+
async function runPullCommand(args) {
|
|
7219
|
+
const opts = parsePullArgs(args);
|
|
7220
|
+
const outcome = await pullSnapshot(opts.cwd);
|
|
7221
|
+
switch (outcome.kind) {
|
|
7222
|
+
case "pulled":
|
|
7223
|
+
console.log(`Pulled commit ${outcome.commitSha.slice(0, 7)}`);
|
|
7224
|
+
console.log(` sha256: ${outcome.snapshotSha256.slice(0, 12)}...`);
|
|
7225
|
+
console.log(` bytes: ${outcome.bytes}`);
|
|
7226
|
+
console.log(` origin: worktree_id=${outcome.sourceWorktreePath}`);
|
|
7227
|
+
console.log(` cloud ts: ${new Date(outcome.cloudTs).toISOString()}`);
|
|
7228
|
+
break;
|
|
7229
|
+
case "up-to-date":
|
|
7230
|
+
console.log(`Already up-to-date (commit ${outcome.commitSha.slice(0, 7)}, sha256 ${outcome.snapshotSha256.slice(0, 12)}...)`);
|
|
7231
|
+
break;
|
|
7232
|
+
case "local-newer":
|
|
7233
|
+
console.log(`Local is newer than cloud \u2014 not pulling.`);
|
|
7234
|
+
console.log(` commit: ${outcome.commitSha.slice(0, 7)}`);
|
|
7235
|
+
console.log(` local ts: ${new Date(outcome.localTs).toISOString()}`);
|
|
7236
|
+
console.log(` cloud ts: ${new Date(outcome.cloudTs).toISOString()}`);
|
|
7237
|
+
break;
|
|
7238
|
+
case "no-cloud-row":
|
|
7239
|
+
console.log(`No cloud snapshot for commit ${outcome.commitSha.slice(0, 7)} \u2014 run \`hivemind graph build\` to create one.`);
|
|
7240
|
+
break;
|
|
7241
|
+
case "skipped-no-auth":
|
|
7242
|
+
console.log(`Skipped: not authenticated (run \`hivemind login\`).`);
|
|
7243
|
+
break;
|
|
7244
|
+
case "skipped-disabled":
|
|
7245
|
+
console.log(`Skipped: HIVEMIND_GRAPH_PULL=0.`);
|
|
7246
|
+
break;
|
|
7247
|
+
case "skipped-no-head":
|
|
7248
|
+
console.log(`Skipped: not in a git repo (\`git rev-parse HEAD\` failed).`);
|
|
5407
7249
|
break;
|
|
7250
|
+
case "error":
|
|
7251
|
+
console.warn(`Pull error (non-fatal): ${outcome.message}`);
|
|
7252
|
+
process.exitCode = 1;
|
|
7253
|
+
break;
|
|
7254
|
+
}
|
|
7255
|
+
}
|
|
7256
|
+
function workTreeIdFor2(cwd) {
|
|
7257
|
+
return createHash6("sha256").update(cwd).digest("hex").slice(0, 16);
|
|
7258
|
+
}
|
|
7259
|
+
function discoverSourceFiles(rootDir) {
|
|
7260
|
+
const out = [];
|
|
7261
|
+
walk(rootDir, out);
|
|
7262
|
+
out.sort();
|
|
7263
|
+
return out;
|
|
7264
|
+
}
|
|
7265
|
+
function walk(dir, out) {
|
|
7266
|
+
let entries;
|
|
7267
|
+
try {
|
|
7268
|
+
entries = readdirSync2(dir, { withFileTypes: true });
|
|
7269
|
+
} catch {
|
|
7270
|
+
return;
|
|
7271
|
+
}
|
|
7272
|
+
for (const entry of entries) {
|
|
7273
|
+
if (DEFAULT_IGNORES.has(entry.name))
|
|
7274
|
+
continue;
|
|
7275
|
+
if (entry.name.startsWith("."))
|
|
7276
|
+
continue;
|
|
7277
|
+
const abs = join27(dir, entry.name);
|
|
7278
|
+
if (entry.isDirectory()) {
|
|
7279
|
+
walk(abs, out);
|
|
7280
|
+
} else if (entry.isFile() && isSourceFile(entry.name)) {
|
|
7281
|
+
out.push(abs);
|
|
5408
7282
|
}
|
|
5409
|
-
default:
|
|
5410
|
-
console.log("Commands: login, logout, whoami, org list, org switch, workspaces, workspace, sessions prune, invite, members, remove, autoupdate");
|
|
5411
7283
|
}
|
|
5412
7284
|
}
|
|
5413
|
-
|
|
5414
|
-
|
|
5415
|
-
|
|
5416
|
-
|
|
5417
|
-
|
|
7285
|
+
function isSourceFile(name) {
|
|
7286
|
+
if (name.endsWith(".d.ts"))
|
|
7287
|
+
return false;
|
|
7288
|
+
return name.endsWith(".ts") || name.endsWith(".tsx");
|
|
7289
|
+
}
|
|
7290
|
+
function toForwardSlash(p) {
|
|
7291
|
+
return sep === "\\" ? p.replace(/\\/g, "/") : p;
|
|
7292
|
+
}
|
|
7293
|
+
function readGitCommit(cwd) {
|
|
7294
|
+
try {
|
|
7295
|
+
return execSync3("git rev-parse HEAD", {
|
|
7296
|
+
cwd,
|
|
7297
|
+
encoding: "utf8",
|
|
7298
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
7299
|
+
}).trim();
|
|
7300
|
+
} catch {
|
|
7301
|
+
return null;
|
|
7302
|
+
}
|
|
7303
|
+
}
|
|
7304
|
+
function readGitBranch(cwd) {
|
|
7305
|
+
try {
|
|
7306
|
+
const out = execSync3("git rev-parse --abbrev-ref HEAD", {
|
|
7307
|
+
cwd,
|
|
7308
|
+
encoding: "utf8",
|
|
7309
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
7310
|
+
}).trim();
|
|
7311
|
+
return out === "" || out === "HEAD" ? null : out;
|
|
7312
|
+
} catch {
|
|
7313
|
+
return null;
|
|
7314
|
+
}
|
|
5418
7315
|
}
|
|
5419
7316
|
|
|
5420
7317
|
// dist/src/commands/dashboard.js
|
|
5421
|
-
import { mkdirSync as
|
|
5422
|
-
import { homedir as
|
|
5423
|
-
import { dirname as
|
|
7318
|
+
import { mkdirSync as mkdirSync16, writeFileSync as writeFileSync18 } from "node:fs";
|
|
7319
|
+
import { homedir as homedir15 } from "node:os";
|
|
7320
|
+
import { dirname as dirname12, join as join35, resolve as resolve5 } from "node:path";
|
|
5424
7321
|
|
|
5425
7322
|
// dist/src/dashboard/data.js
|
|
5426
|
-
import { existsSync as
|
|
5427
|
-
import { homedir as
|
|
5428
|
-
import { join as
|
|
7323
|
+
import { existsSync as existsSync25, readFileSync as readFileSync24, readdirSync as readdirSync4, statSync as statSync3 } from "node:fs";
|
|
7324
|
+
import { homedir as homedir14 } from "node:os";
|
|
7325
|
+
import { join as join33 } from "node:path";
|
|
5429
7326
|
|
|
5430
7327
|
// dist/src/notifications/sources/org-stats.js
|
|
5431
|
-
import { existsSync as
|
|
5432
|
-
import { homedir as
|
|
5433
|
-
import { dirname as
|
|
7328
|
+
import { existsSync as existsSync21, mkdirSync as mkdirSync13, readFileSync as readFileSync21, writeFileSync as writeFileSync16 } from "node:fs";
|
|
7329
|
+
import { homedir as homedir11 } from "node:os";
|
|
7330
|
+
import { dirname as dirname9, join as join28 } from "node:path";
|
|
5434
7331
|
var log5 = (msg) => log2("notifications-org-stats", msg);
|
|
5435
7332
|
var FETCH_TIMEOUT_MS = 1500;
|
|
5436
7333
|
var DEFAULT_API_URL3 = "https://api.deeplake.ai";
|
|
5437
7334
|
var CACHE_TTL_MS = 60 * 60 * 1e3;
|
|
5438
7335
|
function cacheFilePath() {
|
|
5439
|
-
return
|
|
7336
|
+
return join28(homedir11(), ".deeplake", "hivemind-stats-cache.json");
|
|
5440
7337
|
}
|
|
5441
7338
|
function cacheScopeKey(creds) {
|
|
5442
7339
|
return JSON.stringify({
|
|
@@ -5453,11 +7350,11 @@ function scopeFromServer(s) {
|
|
|
5453
7350
|
memorySearchBytes: n(s?.memory_search_bytes)
|
|
5454
7351
|
};
|
|
5455
7352
|
}
|
|
5456
|
-
function
|
|
5457
|
-
if (!
|
|
7353
|
+
function readCache2(scopeKey) {
|
|
7354
|
+
if (!existsSync21(cacheFilePath()))
|
|
5458
7355
|
return {};
|
|
5459
7356
|
try {
|
|
5460
|
-
const parsed = JSON.parse(
|
|
7357
|
+
const parsed = JSON.parse(readFileSync21(cacheFilePath(), "utf-8"));
|
|
5461
7358
|
if (!parsed || typeof parsed !== "object")
|
|
5462
7359
|
return {};
|
|
5463
7360
|
if (parsed.scopeKey !== scopeKey)
|
|
@@ -5476,11 +7373,11 @@ function readCache(scopeKey) {
|
|
|
5476
7373
|
return {};
|
|
5477
7374
|
}
|
|
5478
7375
|
}
|
|
5479
|
-
function
|
|
7376
|
+
function writeCache2(scopeKey, data) {
|
|
5480
7377
|
try {
|
|
5481
|
-
|
|
7378
|
+
mkdirSync13(dirname9(cacheFilePath()), { recursive: true });
|
|
5482
7379
|
const body = { fetchedAt: Date.now(), scopeKey, data };
|
|
5483
|
-
|
|
7380
|
+
writeFileSync16(cacheFilePath(), JSON.stringify(body), "utf-8");
|
|
5484
7381
|
} catch (e) {
|
|
5485
7382
|
log5(`cache write failed: ${e?.message ?? String(e)}`);
|
|
5486
7383
|
}
|
|
@@ -5490,7 +7387,7 @@ async function fetchOrgStats(creds) {
|
|
|
5490
7387
|
return null;
|
|
5491
7388
|
const apiUrl = creds.apiUrl ?? DEFAULT_API_URL3;
|
|
5492
7389
|
const scopeKey = cacheScopeKey(creds);
|
|
5493
|
-
const { fresh, stale } =
|
|
7390
|
+
const { fresh, stale } = readCache2(scopeKey);
|
|
5494
7391
|
if (fresh) {
|
|
5495
7392
|
log5("cache hit \u2014 returning fresh org stats");
|
|
5496
7393
|
return fresh;
|
|
@@ -5519,7 +7416,7 @@ async function fetchOrgStats(creds) {
|
|
|
5519
7416
|
org: scopeFromServer(body.org),
|
|
5520
7417
|
user: scopeFromServer(body.user)
|
|
5521
7418
|
};
|
|
5522
|
-
|
|
7419
|
+
writeCache2(scopeKey, data);
|
|
5523
7420
|
log5(`fetched org stats from ${apiUrl}`);
|
|
5524
7421
|
return data;
|
|
5525
7422
|
} catch (e) {
|
|
@@ -5531,18 +7428,18 @@ async function fetchOrgStats(creds) {
|
|
|
5531
7428
|
}
|
|
5532
7429
|
|
|
5533
7430
|
// dist/src/notifications/usage-tracker.js
|
|
5534
|
-
import { appendFileSync as
|
|
5535
|
-
import { dirname as
|
|
5536
|
-
import { homedir as
|
|
7431
|
+
import { appendFileSync as appendFileSync3, existsSync as existsSync22, mkdirSync as mkdirSync14, readFileSync as readFileSync22, readdirSync as readdirSync3 } from "node:fs";
|
|
7432
|
+
import { dirname as dirname10, join as join29 } from "node:path";
|
|
7433
|
+
import { homedir as homedir12 } from "node:os";
|
|
5537
7434
|
var log6 = (msg) => log2("usage-tracker", msg);
|
|
5538
7435
|
function statsFilePath() {
|
|
5539
|
-
return
|
|
7436
|
+
return join29(homedir12(), ".deeplake", "usage-stats.jsonl");
|
|
5540
7437
|
}
|
|
5541
7438
|
function readUsageRecords() {
|
|
5542
7439
|
try {
|
|
5543
|
-
if (!
|
|
7440
|
+
if (!existsSync22(statsFilePath()))
|
|
5544
7441
|
return [];
|
|
5545
|
-
const raw =
|
|
7442
|
+
const raw = readFileSync22(statsFilePath(), "utf-8");
|
|
5546
7443
|
const out = [];
|
|
5547
7444
|
for (const line of raw.split("\n")) {
|
|
5548
7445
|
const trimmed = line.trim();
|
|
@@ -5579,13 +7476,13 @@ function sumMetric(records, key) {
|
|
|
5579
7476
|
function countUserGeneratedSkills(userName) {
|
|
5580
7477
|
if (!userName)
|
|
5581
7478
|
return 0;
|
|
5582
|
-
const dir =
|
|
5583
|
-
if (!
|
|
7479
|
+
const dir = join29(homedir12(), ".claude", "skills");
|
|
7480
|
+
if (!existsSync22(dir))
|
|
5584
7481
|
return 0;
|
|
5585
7482
|
const suffix = `--${userName}`;
|
|
5586
7483
|
try {
|
|
5587
7484
|
let count = 0;
|
|
5588
|
-
for (const name of
|
|
7485
|
+
for (const name of readdirSync3(dir)) {
|
|
5589
7486
|
const idx = name.lastIndexOf(suffix);
|
|
5590
7487
|
if (idx > 0 && idx + suffix.length === name.length)
|
|
5591
7488
|
count += 1;
|
|
@@ -5598,21 +7495,19 @@ function countUserGeneratedSkills(userName) {
|
|
|
5598
7495
|
}
|
|
5599
7496
|
|
|
5600
7497
|
// dist/src/skillify/state.js
|
|
5601
|
-
import { readFileSync as
|
|
5602
|
-
import {
|
|
5603
|
-
import { createHash } from "node:crypto";
|
|
5604
|
-
import { join as join24, basename } from "node:path";
|
|
7498
|
+
import { readFileSync as readFileSync23, writeFileSync as writeFileSync17, writeSync, mkdirSync as mkdirSync15, renameSync as renameSync9, rmdirSync, existsSync as existsSync24, lstatSync as lstatSync3, unlinkSync as unlinkSync9, openSync as openSync2, closeSync as closeSync2 } from "node:fs";
|
|
7499
|
+
import { join as join32 } from "node:path";
|
|
5605
7500
|
|
|
5606
7501
|
// dist/src/skillify/legacy-migration.js
|
|
5607
|
-
import { existsSync as
|
|
5608
|
-
import { dirname as
|
|
7502
|
+
import { existsSync as existsSync23, renameSync as renameSync8 } from "node:fs";
|
|
7503
|
+
import { dirname as dirname11, join as join31 } from "node:path";
|
|
5609
7504
|
|
|
5610
7505
|
// dist/src/skillify/state-dir.js
|
|
5611
|
-
import { homedir as
|
|
5612
|
-
import { join as
|
|
7506
|
+
import { homedir as homedir13 } from "node:os";
|
|
7507
|
+
import { join as join30 } from "node:path";
|
|
5613
7508
|
function getStateDir() {
|
|
5614
7509
|
const override = process.env.HIVEMIND_STATE_DIR?.trim();
|
|
5615
|
-
return override && override.length > 0 ? override :
|
|
7510
|
+
return override && override.length > 0 ? override : join30(homedir13(), ".deeplake", "state", "skillify");
|
|
5616
7511
|
}
|
|
5617
7512
|
|
|
5618
7513
|
// dist/src/skillify/legacy-migration.js
|
|
@@ -5625,13 +7520,13 @@ function migrateLegacyStateDir() {
|
|
|
5625
7520
|
return;
|
|
5626
7521
|
attempted = true;
|
|
5627
7522
|
const current = getStateDir();
|
|
5628
|
-
const legacy =
|
|
5629
|
-
if (!
|
|
7523
|
+
const legacy = join31(dirname11(current), "skilify");
|
|
7524
|
+
if (!existsSync23(legacy))
|
|
5630
7525
|
return;
|
|
5631
|
-
if (
|
|
7526
|
+
if (existsSync23(current))
|
|
5632
7527
|
return;
|
|
5633
7528
|
try {
|
|
5634
|
-
|
|
7529
|
+
renameSync8(legacy, current);
|
|
5635
7530
|
dlog(`migrated ${legacy} -> ${current}`);
|
|
5636
7531
|
} catch (err) {
|
|
5637
7532
|
const code = err.code;
|
|
@@ -5649,54 +7544,13 @@ var TRIGGER_THRESHOLD = (() => {
|
|
|
5649
7544
|
const n = Number(process.env.HIVEMIND_SKILLIFY_EVERY_N_TURNS ?? "");
|
|
5650
7545
|
return Number.isInteger(n) && n > 0 ? n : 20;
|
|
5651
7546
|
})();
|
|
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
7547
|
|
|
5694
7548
|
// dist/src/dashboard/data.js
|
|
5695
7549
|
var log7 = (msg) => log2("dashboard-data", msg);
|
|
5696
7550
|
var BYTES_PER_TOKEN = 4;
|
|
5697
7551
|
var SAVINGS_MULTIPLIER = 1.7;
|
|
5698
|
-
function
|
|
5699
|
-
return process.env.HIVEMIND_GRAPHS_HOME ??
|
|
7552
|
+
function graphsRoot2() {
|
|
7553
|
+
return process.env.HIVEMIND_GRAPHS_HOME ?? join33(homedir14(), ".hivemind", "graphs");
|
|
5700
7554
|
}
|
|
5701
7555
|
function bytesToSavedTokens(bytes) {
|
|
5702
7556
|
if (!Number.isFinite(bytes) || bytes <= 0)
|
|
@@ -5704,18 +7558,18 @@ function bytesToSavedTokens(bytes) {
|
|
|
5704
7558
|
const delivered = bytes / BYTES_PER_TOKEN;
|
|
5705
7559
|
return (SAVINGS_MULTIPLIER - 1) * delivered;
|
|
5706
7560
|
}
|
|
5707
|
-
function resolveSnapshot(
|
|
5708
|
-
const snapshotsDir =
|
|
5709
|
-
if (!
|
|
7561
|
+
function resolveSnapshot(repoDir2) {
|
|
7562
|
+
const snapshotsDir = join33(repoDir2, "snapshots");
|
|
7563
|
+
if (!existsSync25(snapshotsDir))
|
|
5710
7564
|
return null;
|
|
5711
7565
|
let snapshotPath = null;
|
|
5712
|
-
const pointer =
|
|
5713
|
-
if (
|
|
7566
|
+
const pointer = join33(repoDir2, "latest-commit.txt");
|
|
7567
|
+
if (existsSync25(pointer)) {
|
|
5714
7568
|
try {
|
|
5715
|
-
const sha =
|
|
7569
|
+
const sha = readFileSync24(pointer, "utf-8").trim();
|
|
5716
7570
|
if (sha) {
|
|
5717
|
-
const candidate =
|
|
5718
|
-
if (
|
|
7571
|
+
const candidate = join33(snapshotsDir, `${sha}.json`);
|
|
7572
|
+
if (existsSync25(candidate))
|
|
5719
7573
|
snapshotPath = candidate;
|
|
5720
7574
|
else
|
|
5721
7575
|
log7(`latest-commit.txt points at missing ${sha}.json \u2014 scanning snapshots/`);
|
|
@@ -5726,8 +7580,8 @@ function resolveSnapshot(repoDir) {
|
|
|
5726
7580
|
}
|
|
5727
7581
|
if (!snapshotPath) {
|
|
5728
7582
|
try {
|
|
5729
|
-
const candidates =
|
|
5730
|
-
const full =
|
|
7583
|
+
const candidates = readdirSync4(snapshotsDir).filter((name) => name.endsWith(".json")).map((name) => {
|
|
7584
|
+
const full = join33(snapshotsDir, name);
|
|
5731
7585
|
return { full, mtime: statSync3(full).mtimeMs };
|
|
5732
7586
|
}).sort((a, b) => b.mtime - a.mtime);
|
|
5733
7587
|
if (candidates.length > 0)
|
|
@@ -5740,7 +7594,7 @@ function resolveSnapshot(repoDir) {
|
|
|
5740
7594
|
return null;
|
|
5741
7595
|
let raw;
|
|
5742
7596
|
try {
|
|
5743
|
-
raw =
|
|
7597
|
+
raw = readFileSync24(snapshotPath, "utf-8");
|
|
5744
7598
|
} catch (e) {
|
|
5745
7599
|
log7(`snapshot read failed: ${e?.message ?? String(e)}`);
|
|
5746
7600
|
return null;
|
|
@@ -5810,8 +7664,8 @@ async function loadKpis(creds) {
|
|
|
5810
7664
|
async function loadDashboardData(opts = {}) {
|
|
5811
7665
|
const cwd = opts.cwd ?? process.cwd();
|
|
5812
7666
|
const { key: repoKey, project: repoProject } = deriveProjectKey(cwd);
|
|
5813
|
-
const
|
|
5814
|
-
const graph = resolveSnapshot(
|
|
7667
|
+
const repoDir2 = join33(opts.graphsHome ?? graphsRoot2(), repoKey);
|
|
7668
|
+
const graph = resolveSnapshot(repoDir2);
|
|
5815
7669
|
const creds = opts.creds === void 0 ? loadCredentials() : opts.creds;
|
|
5816
7670
|
const kpis = await loadKpis(creds);
|
|
5817
7671
|
return {
|
|
@@ -5827,7 +7681,7 @@ async function loadDashboardData(opts = {}) {
|
|
|
5827
7681
|
import { spawn } from "node:child_process";
|
|
5828
7682
|
import { accessSync, constants as fsConstants, statSync as statSync4 } from "node:fs";
|
|
5829
7683
|
import { platform as nodePlatform } from "node:os";
|
|
5830
|
-
import { delimiter, join as
|
|
7684
|
+
import { delimiter, join as join34 } from "node:path";
|
|
5831
7685
|
function resolveOpenPlatform() {
|
|
5832
7686
|
const p = nodePlatform();
|
|
5833
7687
|
if (p === "linux" || p === "darwin" || p === "win32")
|
|
@@ -5854,7 +7708,7 @@ function findBinaryOnPath(name) {
|
|
|
5854
7708
|
if (!dir)
|
|
5855
7709
|
continue;
|
|
5856
7710
|
for (const ext of exts) {
|
|
5857
|
-
const candidate =
|
|
7711
|
+
const candidate = join34(dir, name + ext);
|
|
5858
7712
|
try {
|
|
5859
7713
|
const st = statSync4(candidate);
|
|
5860
7714
|
if (!st.isFile())
|
|
@@ -6193,7 +8047,7 @@ function handleRequest(html) {
|
|
|
6193
8047
|
};
|
|
6194
8048
|
}
|
|
6195
8049
|
function tryListen(server, host, port) {
|
|
6196
|
-
return new Promise((
|
|
8050
|
+
return new Promise((resolve6, reject) => {
|
|
6197
8051
|
const onError = (err) => {
|
|
6198
8052
|
server.off("listening", onListening);
|
|
6199
8053
|
reject(err);
|
|
@@ -6205,7 +8059,7 @@ function tryListen(server, host, port) {
|
|
|
6205
8059
|
reject(new Error("server bound to a non-IP address"));
|
|
6206
8060
|
return;
|
|
6207
8061
|
}
|
|
6208
|
-
|
|
8062
|
+
resolve6(addr.port);
|
|
6209
8063
|
};
|
|
6210
8064
|
server.once("error", onError);
|
|
6211
8065
|
server.once("listening", onListening);
|
|
@@ -6231,22 +8085,22 @@ async function serveDashboardHtml(opts) {
|
|
|
6231
8085
|
}
|
|
6232
8086
|
function makeHandle(server, host, port) {
|
|
6233
8087
|
let resolveStopped;
|
|
6234
|
-
const stopped = new Promise((
|
|
6235
|
-
resolveStopped =
|
|
8088
|
+
const stopped = new Promise((resolve6) => {
|
|
8089
|
+
resolveStopped = resolve6;
|
|
6236
8090
|
});
|
|
6237
8091
|
server.on("close", () => resolveStopped());
|
|
6238
8092
|
return {
|
|
6239
8093
|
host,
|
|
6240
8094
|
port,
|
|
6241
8095
|
stopped,
|
|
6242
|
-
close: () => new Promise((
|
|
6243
|
-
server.close((err) => err ? reject(err) :
|
|
8096
|
+
close: () => new Promise((resolve6, reject) => {
|
|
8097
|
+
server.close((err) => err ? reject(err) : resolve6());
|
|
6244
8098
|
})
|
|
6245
8099
|
};
|
|
6246
8100
|
}
|
|
6247
8101
|
|
|
6248
8102
|
// dist/src/commands/dashboard.js
|
|
6249
|
-
var
|
|
8103
|
+
var USAGE2 = `hivemind dashboard \u2014 codebase graph + KPI dashboard (HTML)
|
|
6250
8104
|
|
|
6251
8105
|
Usage:
|
|
6252
8106
|
hivemind dashboard [--cwd <path>] [--out <path>] [--no-open]
|
|
@@ -6364,7 +8218,7 @@ function parseDashboardArgs(args) {
|
|
|
6364
8218
|
};
|
|
6365
8219
|
}
|
|
6366
8220
|
function defaultDashboardOutPath(repoKey) {
|
|
6367
|
-
return
|
|
8221
|
+
return join35(homedir15(), ".hivemind", "dashboards", repoKey, "index.html");
|
|
6368
8222
|
}
|
|
6369
8223
|
async function runDashboardCommand(rawArgs, runOpts = {}) {
|
|
6370
8224
|
const out = runOpts.out ?? ((s) => {
|
|
@@ -6376,13 +8230,13 @@ async function runDashboardCommand(rawArgs, runOpts = {}) {
|
|
|
6376
8230
|
const opener = runOpts.opener ?? openInBrowser;
|
|
6377
8231
|
const parsed = parseDashboardArgs(rawArgs);
|
|
6378
8232
|
if (parsed.help) {
|
|
6379
|
-
out(
|
|
8233
|
+
out(USAGE2);
|
|
6380
8234
|
return 0;
|
|
6381
8235
|
}
|
|
6382
8236
|
if (parsed.error || !parsed.args) {
|
|
6383
8237
|
err(`hivemind dashboard: ${parsed.error ?? "invalid arguments"}
|
|
6384
8238
|
`);
|
|
6385
|
-
err(
|
|
8239
|
+
err(USAGE2);
|
|
6386
8240
|
return 2;
|
|
6387
8241
|
}
|
|
6388
8242
|
const { cwd, outPath, open } = parsed.args;
|
|
@@ -6396,10 +8250,10 @@ async function runDashboardCommand(rawArgs, runOpts = {}) {
|
|
|
6396
8250
|
}
|
|
6397
8251
|
const html = renderDashboardHtml(data);
|
|
6398
8252
|
const finalOut = outPath || defaultDashboardOutPath(data.repoKey);
|
|
6399
|
-
const absOut =
|
|
8253
|
+
const absOut = resolve5(finalOut);
|
|
6400
8254
|
try {
|
|
6401
|
-
|
|
6402
|
-
|
|
8255
|
+
mkdirSync16(dirname12(absOut), { recursive: true });
|
|
8256
|
+
writeFileSync18(absOut, html, "utf-8");
|
|
6403
8257
|
} catch (e) {
|
|
6404
8258
|
err(`hivemind dashboard: failed to write ${absOut}: ${e?.message ?? String(e)}
|
|
6405
8259
|
`);
|
|
@@ -6478,24 +8332,24 @@ function defaultOnSignal(signal, handler) {
|
|
|
6478
8332
|
}
|
|
6479
8333
|
|
|
6480
8334
|
// dist/src/commands/skillify.js
|
|
6481
|
-
import { readdirSync as
|
|
6482
|
-
import { homedir as
|
|
6483
|
-
import { dirname as
|
|
8335
|
+
import { readdirSync as readdirSync8, existsSync as existsSync36, readFileSync as readFileSync32, mkdirSync as mkdirSync23, renameSync as renameSync12 } from "node:fs";
|
|
8336
|
+
import { homedir as homedir24 } from "node:os";
|
|
8337
|
+
import { dirname as dirname17, join as join46 } from "node:path";
|
|
6484
8338
|
|
|
6485
8339
|
// dist/src/skillify/scope-config.js
|
|
6486
|
-
import { existsSync as
|
|
6487
|
-
import { join as
|
|
8340
|
+
import { existsSync as existsSync26, mkdirSync as mkdirSync17, readFileSync as readFileSync25, writeFileSync as writeFileSync19 } from "node:fs";
|
|
8341
|
+
import { join as join36 } from "node:path";
|
|
6488
8342
|
function configPath() {
|
|
6489
|
-
return
|
|
8343
|
+
return join36(getStateDir(), "config.json");
|
|
6490
8344
|
}
|
|
6491
8345
|
var DEFAULT = { scope: "me", team: [], install: "project" };
|
|
6492
8346
|
function loadScopeConfig() {
|
|
6493
8347
|
migrateLegacyStateDir();
|
|
6494
8348
|
const CONFIG_PATH2 = configPath();
|
|
6495
|
-
if (!
|
|
8349
|
+
if (!existsSync26(CONFIG_PATH2))
|
|
6496
8350
|
return DEFAULT;
|
|
6497
8351
|
try {
|
|
6498
|
-
const raw = JSON.parse(
|
|
8352
|
+
const raw = JSON.parse(readFileSync25(CONFIG_PATH2, "utf-8"));
|
|
6499
8353
|
const scope = raw.scope === "team" ? "team" : raw.scope === "org" ? "team" : "me";
|
|
6500
8354
|
const team = Array.isArray(raw.team) ? raw.team.filter((s) => typeof s === "string") : [];
|
|
6501
8355
|
const install = raw.install === "global" ? "global" : "project";
|
|
@@ -6506,19 +8360,19 @@ function loadScopeConfig() {
|
|
|
6506
8360
|
}
|
|
6507
8361
|
function saveScopeConfig(cfg) {
|
|
6508
8362
|
migrateLegacyStateDir();
|
|
6509
|
-
|
|
6510
|
-
|
|
8363
|
+
mkdirSync17(getStateDir(), { recursive: true });
|
|
8364
|
+
writeFileSync19(configPath(), JSON.stringify(cfg, null, 2));
|
|
6511
8365
|
}
|
|
6512
8366
|
|
|
6513
8367
|
// dist/src/skillify/pull.js
|
|
6514
|
-
import { existsSync as
|
|
6515
|
-
import { homedir as
|
|
6516
|
-
import { dirname as
|
|
8368
|
+
import { existsSync as existsSync30, readFileSync as readFileSync28, writeFileSync as writeFileSync22, mkdirSync as mkdirSync20, renameSync as renameSync11, lstatSync as lstatSync5, readlinkSync as readlinkSync2, symlinkSync as symlinkSync2, unlinkSync as unlinkSync11 } from "node:fs";
|
|
8369
|
+
import { homedir as homedir18 } from "node:os";
|
|
8370
|
+
import { dirname as dirname14, join as join40 } from "node:path";
|
|
6517
8371
|
|
|
6518
8372
|
// dist/src/skillify/skill-writer.js
|
|
6519
|
-
import { existsSync as
|
|
6520
|
-
import { homedir as
|
|
6521
|
-
import { join as
|
|
8373
|
+
import { existsSync as existsSync27, mkdirSync as mkdirSync18, readFileSync as readFileSync26, readdirSync as readdirSync5, statSync as statSync5, writeFileSync as writeFileSync20 } from "node:fs";
|
|
8374
|
+
import { homedir as homedir16 } from "node:os";
|
|
8375
|
+
import { join as join37 } from "node:path";
|
|
6522
8376
|
function assertValidSkillName(name) {
|
|
6523
8377
|
if (typeof name !== "string" || name.length === 0) {
|
|
6524
8378
|
throw new Error(`invalid skill name: empty or non-string`);
|
|
@@ -6534,10 +8388,10 @@ function assertValidSkillName(name) {
|
|
|
6534
8388
|
}
|
|
6535
8389
|
}
|
|
6536
8390
|
function skillDir(skillsRoot, name) {
|
|
6537
|
-
return
|
|
8391
|
+
return join37(skillsRoot, name);
|
|
6538
8392
|
}
|
|
6539
8393
|
function skillPath(skillsRoot, name) {
|
|
6540
|
-
return
|
|
8394
|
+
return join37(skillDir(skillsRoot, name), "SKILL.md");
|
|
6541
8395
|
}
|
|
6542
8396
|
function renderFrontmatter(fm) {
|
|
6543
8397
|
const lines = ["---"];
|
|
@@ -6615,10 +8469,10 @@ function writeNewSkill(args) {
|
|
|
6615
8469
|
assertValidSkillName(args.name);
|
|
6616
8470
|
const dir = skillDir(args.skillsRoot, args.name);
|
|
6617
8471
|
const path = skillPath(args.skillsRoot, args.name);
|
|
6618
|
-
if (
|
|
8472
|
+
if (existsSync27(path)) {
|
|
6619
8473
|
throw new Error(`skill already exists at ${path}; use mergeSkill`);
|
|
6620
8474
|
}
|
|
6621
|
-
|
|
8475
|
+
mkdirSync18(dir, { recursive: true });
|
|
6622
8476
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
6623
8477
|
const author = args.author && args.author.length > 0 ? args.author : void 0;
|
|
6624
8478
|
const contributors = author ? [author] : [];
|
|
@@ -6638,7 +8492,7 @@ function writeNewSkill(args) {
|
|
|
6638
8492
|
|
|
6639
8493
|
${args.body.trim()}
|
|
6640
8494
|
`;
|
|
6641
|
-
|
|
8495
|
+
writeFileSync20(path, text);
|
|
6642
8496
|
return {
|
|
6643
8497
|
path,
|
|
6644
8498
|
action: "created",
|
|
@@ -6650,40 +8504,40 @@ ${args.body.trim()}
|
|
|
6650
8504
|
};
|
|
6651
8505
|
}
|
|
6652
8506
|
function listSkills(skillsRoot) {
|
|
6653
|
-
if (!
|
|
8507
|
+
if (!existsSync27(skillsRoot))
|
|
6654
8508
|
return [];
|
|
6655
8509
|
const out = [];
|
|
6656
|
-
for (const name of
|
|
6657
|
-
const skillFile =
|
|
6658
|
-
if (
|
|
6659
|
-
out.push({ name, body:
|
|
8510
|
+
for (const name of readdirSync5(skillsRoot)) {
|
|
8511
|
+
const skillFile = join37(skillsRoot, name, "SKILL.md");
|
|
8512
|
+
if (existsSync27(skillFile) && statSync5(skillFile).isFile()) {
|
|
8513
|
+
out.push({ name, body: readFileSync26(skillFile, "utf-8") });
|
|
6660
8514
|
}
|
|
6661
8515
|
}
|
|
6662
8516
|
return out;
|
|
6663
8517
|
}
|
|
6664
8518
|
function resolveSkillsRoot(install, cwd) {
|
|
6665
8519
|
if (install === "global") {
|
|
6666
|
-
return
|
|
8520
|
+
return join37(homedir16(), ".claude", "skills");
|
|
6667
8521
|
}
|
|
6668
|
-
return
|
|
8522
|
+
return join37(cwd, ".claude", "skills");
|
|
6669
8523
|
}
|
|
6670
8524
|
|
|
6671
8525
|
// dist/src/skillify/manifest.js
|
|
6672
|
-
import { existsSync as
|
|
6673
|
-
import { dirname as
|
|
8526
|
+
import { existsSync as existsSync28, lstatSync as lstatSync4, mkdirSync as mkdirSync19, readFileSync as readFileSync27, renameSync as renameSync10, unlinkSync as unlinkSync10, writeFileSync as writeFileSync21 } from "node:fs";
|
|
8527
|
+
import { dirname as dirname13, join as join38 } from "node:path";
|
|
6674
8528
|
function emptyManifest() {
|
|
6675
8529
|
return { version: 1, entries: [] };
|
|
6676
8530
|
}
|
|
6677
8531
|
function manifestPath() {
|
|
6678
|
-
return
|
|
8532
|
+
return join38(getStateDir(), "pulled.json");
|
|
6679
8533
|
}
|
|
6680
8534
|
function loadManifest(path = manifestPath()) {
|
|
6681
8535
|
migrateLegacyStateDir();
|
|
6682
|
-
if (!
|
|
8536
|
+
if (!existsSync28(path))
|
|
6683
8537
|
return emptyManifest();
|
|
6684
8538
|
let raw;
|
|
6685
8539
|
try {
|
|
6686
|
-
raw =
|
|
8540
|
+
raw = readFileSync27(path, "utf-8");
|
|
6687
8541
|
} catch {
|
|
6688
8542
|
return emptyManifest();
|
|
6689
8543
|
}
|
|
@@ -6730,10 +8584,10 @@ function loadManifest(path = manifestPath()) {
|
|
|
6730
8584
|
}
|
|
6731
8585
|
function saveManifest(m, path = manifestPath()) {
|
|
6732
8586
|
migrateLegacyStateDir();
|
|
6733
|
-
|
|
8587
|
+
mkdirSync19(dirname13(path), { recursive: true });
|
|
6734
8588
|
const tmp = `${path}.tmp`;
|
|
6735
|
-
|
|
6736
|
-
|
|
8589
|
+
writeFileSync21(tmp, JSON.stringify(m, null, 2) + "\n", { mode: 384 });
|
|
8590
|
+
renameSync10(tmp, path);
|
|
6737
8591
|
}
|
|
6738
8592
|
function recordPull(entry, path = manifestPath()) {
|
|
6739
8593
|
const m = loadManifest(path);
|
|
@@ -6765,7 +8619,7 @@ function unlinkSymlinks(paths) {
|
|
|
6765
8619
|
if (!st.isSymbolicLink())
|
|
6766
8620
|
continue;
|
|
6767
8621
|
try {
|
|
6768
|
-
|
|
8622
|
+
unlinkSync10(path);
|
|
6769
8623
|
} catch {
|
|
6770
8624
|
}
|
|
6771
8625
|
}
|
|
@@ -6775,7 +8629,7 @@ function pruneOrphanedEntries(path = manifestPath()) {
|
|
|
6775
8629
|
const live = [];
|
|
6776
8630
|
let pruned = 0;
|
|
6777
8631
|
for (const e of m.entries) {
|
|
6778
|
-
if (
|
|
8632
|
+
if (existsSync28(join38(e.installRoot, e.dirName))) {
|
|
6779
8633
|
live.push(e);
|
|
6780
8634
|
continue;
|
|
6781
8635
|
}
|
|
@@ -6788,26 +8642,26 @@ function pruneOrphanedEntries(path = manifestPath()) {
|
|
|
6788
8642
|
}
|
|
6789
8643
|
|
|
6790
8644
|
// dist/src/skillify/agent-roots.js
|
|
6791
|
-
import { existsSync as
|
|
6792
|
-
import { homedir as
|
|
6793
|
-
import { join as
|
|
8645
|
+
import { existsSync as existsSync29 } from "node:fs";
|
|
8646
|
+
import { homedir as homedir17 } from "node:os";
|
|
8647
|
+
import { join as join39 } from "node:path";
|
|
6794
8648
|
function resolveDetected(home) {
|
|
6795
8649
|
const out = [];
|
|
6796
|
-
const codexInstalled =
|
|
6797
|
-
const piInstalled =
|
|
6798
|
-
const hermesInstalled =
|
|
8650
|
+
const codexInstalled = existsSync29(join39(home, ".codex"));
|
|
8651
|
+
const piInstalled = existsSync29(join39(home, ".pi", "agent"));
|
|
8652
|
+
const hermesInstalled = existsSync29(join39(home, ".hermes"));
|
|
6799
8653
|
if (codexInstalled || piInstalled) {
|
|
6800
|
-
out.push(
|
|
8654
|
+
out.push(join39(home, ".agents", "skills"));
|
|
6801
8655
|
}
|
|
6802
8656
|
if (hermesInstalled) {
|
|
6803
|
-
out.push(
|
|
8657
|
+
out.push(join39(home, ".hermes", "skills"));
|
|
6804
8658
|
}
|
|
6805
8659
|
if (piInstalled) {
|
|
6806
|
-
out.push(
|
|
8660
|
+
out.push(join39(home, ".pi", "agent", "skills"));
|
|
6807
8661
|
}
|
|
6808
8662
|
return out;
|
|
6809
8663
|
}
|
|
6810
|
-
function detectAgentSkillsRoots(canonicalRoot, home =
|
|
8664
|
+
function detectAgentSkillsRoots(canonicalRoot, home = homedir17()) {
|
|
6811
8665
|
return resolveDetected(home).filter((p) => p !== canonicalRoot);
|
|
6812
8666
|
}
|
|
6813
8667
|
|
|
@@ -6851,15 +8705,15 @@ function isMissingTableError(message) {
|
|
|
6851
8705
|
}
|
|
6852
8706
|
function resolvePullDestination(install, cwd) {
|
|
6853
8707
|
if (install === "global")
|
|
6854
|
-
return
|
|
8708
|
+
return join40(homedir18(), ".claude", "skills");
|
|
6855
8709
|
if (!cwd)
|
|
6856
8710
|
throw new Error("install=project requires a cwd");
|
|
6857
|
-
return
|
|
8711
|
+
return join40(cwd, ".claude", "skills");
|
|
6858
8712
|
}
|
|
6859
8713
|
function fanOutSymlinks(canonicalDir, dirName, agentRoots) {
|
|
6860
8714
|
const out = [];
|
|
6861
8715
|
for (const root of agentRoots) {
|
|
6862
|
-
const link =
|
|
8716
|
+
const link = join40(root, dirName);
|
|
6863
8717
|
let existing;
|
|
6864
8718
|
try {
|
|
6865
8719
|
existing = lstatSync5(link);
|
|
@@ -6881,13 +8735,13 @@ function fanOutSymlinks(canonicalDir, dirName, agentRoots) {
|
|
|
6881
8735
|
continue;
|
|
6882
8736
|
}
|
|
6883
8737
|
try {
|
|
6884
|
-
|
|
8738
|
+
unlinkSync11(link);
|
|
6885
8739
|
} catch {
|
|
6886
8740
|
continue;
|
|
6887
8741
|
}
|
|
6888
8742
|
}
|
|
6889
8743
|
try {
|
|
6890
|
-
|
|
8744
|
+
mkdirSync20(dirname14(link), { recursive: true });
|
|
6891
8745
|
symlinkSync2(canonicalDir, link, "dir");
|
|
6892
8746
|
out.push(link);
|
|
6893
8747
|
} catch {
|
|
@@ -6902,8 +8756,8 @@ function backfillSymlinks(installRoot) {
|
|
|
6902
8756
|
return;
|
|
6903
8757
|
const detected = detectAgentSkillsRoots(installRoot);
|
|
6904
8758
|
for (const entry of entries) {
|
|
6905
|
-
const canonical =
|
|
6906
|
-
if (!
|
|
8759
|
+
const canonical = join40(entry.installRoot, entry.dirName);
|
|
8760
|
+
if (!existsSync30(canonical))
|
|
6907
8761
|
continue;
|
|
6908
8762
|
const fresh = fanOutSymlinks(canonical, entry.dirName, detected);
|
|
6909
8763
|
if (sameSorted(fresh, entry.symlinks))
|
|
@@ -7013,10 +8867,10 @@ function renderFrontmatter2(fm) {
|
|
|
7013
8867
|
return lines.join("\n");
|
|
7014
8868
|
}
|
|
7015
8869
|
function readLocalVersion(path) {
|
|
7016
|
-
if (!
|
|
8870
|
+
if (!existsSync30(path))
|
|
7017
8871
|
return null;
|
|
7018
8872
|
try {
|
|
7019
|
-
const text =
|
|
8873
|
+
const text = readFileSync28(path, "utf-8");
|
|
7020
8874
|
const parsed = parseFrontmatter(text);
|
|
7021
8875
|
if (!parsed)
|
|
7022
8876
|
return null;
|
|
@@ -7111,8 +8965,8 @@ async function runPull(opts) {
|
|
|
7111
8965
|
summary.skipped++;
|
|
7112
8966
|
continue;
|
|
7113
8967
|
}
|
|
7114
|
-
const skillDir2 =
|
|
7115
|
-
const skillFile =
|
|
8968
|
+
const skillDir2 = join40(root, dirName);
|
|
8969
|
+
const skillFile = join40(skillDir2, "SKILL.md");
|
|
7116
8970
|
const remoteVersion = Number(row.version ?? 1);
|
|
7117
8971
|
const localVersion = readLocalVersion(skillFile);
|
|
7118
8972
|
const action = decideAction({
|
|
@@ -7123,14 +8977,14 @@ async function runPull(opts) {
|
|
|
7123
8977
|
});
|
|
7124
8978
|
let manifestError;
|
|
7125
8979
|
if (action === "wrote") {
|
|
7126
|
-
|
|
7127
|
-
if (
|
|
8980
|
+
mkdirSync20(skillDir2, { recursive: true });
|
|
8981
|
+
if (existsSync30(skillFile)) {
|
|
7128
8982
|
try {
|
|
7129
|
-
|
|
8983
|
+
renameSync11(skillFile, `${skillFile}.bak`);
|
|
7130
8984
|
} catch {
|
|
7131
8985
|
}
|
|
7132
8986
|
}
|
|
7133
|
-
|
|
8987
|
+
writeFileSync22(skillFile, renderSkillFile(row));
|
|
7134
8988
|
const symlinks = opts.install === "global" ? fanOutSymlinks(skillDir2, dirName, detectAgentSkillsRoots(root)) : [];
|
|
7135
8989
|
try {
|
|
7136
8990
|
recordPull({
|
|
@@ -7172,15 +9026,15 @@ async function runPull(opts) {
|
|
|
7172
9026
|
}
|
|
7173
9027
|
|
|
7174
9028
|
// dist/src/skillify/unpull.js
|
|
7175
|
-
import { existsSync as
|
|
7176
|
-
import { homedir as
|
|
7177
|
-
import { join as
|
|
9029
|
+
import { existsSync as existsSync31, readdirSync as readdirSync6, rmSync as rmSync5, statSync as statSync6 } from "node:fs";
|
|
9030
|
+
import { homedir as homedir19 } from "node:os";
|
|
9031
|
+
import { join as join41 } from "node:path";
|
|
7178
9032
|
function resolveUnpullRoot(install, cwd) {
|
|
7179
9033
|
if (install === "global")
|
|
7180
|
-
return
|
|
9034
|
+
return join41(homedir19(), ".claude", "skills");
|
|
7181
9035
|
if (!cwd)
|
|
7182
9036
|
throw new Error("cwd required when install === 'project'");
|
|
7183
|
-
return
|
|
9037
|
+
return join41(cwd, ".claude", "skills");
|
|
7184
9038
|
}
|
|
7185
9039
|
function runUnpull(opts) {
|
|
7186
9040
|
const root = resolveUnpullRoot(opts.install, opts.cwd);
|
|
@@ -7203,8 +9057,8 @@ function runUnpull(opts) {
|
|
|
7203
9057
|
const entries = entriesForRoot(manifest, opts.install, root);
|
|
7204
9058
|
for (const entry of entries) {
|
|
7205
9059
|
summary.scanned++;
|
|
7206
|
-
const path =
|
|
7207
|
-
if (!
|
|
9060
|
+
const path = join41(root, entry.dirName);
|
|
9061
|
+
if (!existsSync31(path)) {
|
|
7208
9062
|
if (!opts.dryRun) {
|
|
7209
9063
|
unlinkSymlinks(entry.symlinks);
|
|
7210
9064
|
removePullEntry(opts.install, entry.installRoot, entry.dirName);
|
|
@@ -7257,12 +9111,12 @@ function runUnpull(opts) {
|
|
|
7257
9111
|
}
|
|
7258
9112
|
summary.entries.push(result);
|
|
7259
9113
|
}
|
|
7260
|
-
if (
|
|
9114
|
+
if (existsSync31(root) && (opts.all || opts.legacyCleanup)) {
|
|
7261
9115
|
const manifestDirNames = new Set(entries.map((e) => e.dirName));
|
|
7262
|
-
for (const dirName of
|
|
9116
|
+
for (const dirName of readdirSync6(root)) {
|
|
7263
9117
|
if (manifestDirNames.has(dirName))
|
|
7264
9118
|
continue;
|
|
7265
|
-
const path =
|
|
9119
|
+
const path = join41(root, dirName);
|
|
7266
9120
|
let st;
|
|
7267
9121
|
try {
|
|
7268
9122
|
st = statSync6(path);
|
|
@@ -7341,30 +9195,30 @@ function decideTargetForManifestEntry(entry, opts, userFilter, haveUserFilter) {
|
|
|
7341
9195
|
|
|
7342
9196
|
// dist/src/commands/mine-local.js
|
|
7343
9197
|
import { spawn as spawn2 } from "node:child_process";
|
|
7344
|
-
import { existsSync as
|
|
7345
|
-
import { homedir as
|
|
7346
|
-
import { basename as basename2, dirname as
|
|
9198
|
+
import { existsSync as existsSync35, mkdirSync as mkdirSync22, readFileSync as readFileSync31, writeFileSync as writeFileSync24 } from "node:fs";
|
|
9199
|
+
import { homedir as homedir23 } from "node:os";
|
|
9200
|
+
import { basename as basename2, dirname as dirname16, join as join45 } from "node:path";
|
|
7347
9201
|
|
|
7348
9202
|
// dist/src/skillify/local-source.js
|
|
7349
|
-
import { readdirSync as
|
|
7350
|
-
import { homedir as
|
|
7351
|
-
import { join as
|
|
7352
|
-
var HOME2 =
|
|
9203
|
+
import { readdirSync as readdirSync7, readFileSync as readFileSync29, existsSync as existsSync32, statSync as statSync7 } from "node:fs";
|
|
9204
|
+
import { homedir as homedir20 } from "node:os";
|
|
9205
|
+
import { join as join42 } from "node:path";
|
|
9206
|
+
var HOME2 = homedir20();
|
|
7353
9207
|
function encodeCwdClaudeCode(cwd) {
|
|
7354
9208
|
return cwd.replace(/[/_]/g, "-");
|
|
7355
9209
|
}
|
|
7356
9210
|
function detectInstalledAgents() {
|
|
7357
9211
|
const installs = [];
|
|
7358
|
-
const claudeRoot =
|
|
7359
|
-
if (
|
|
9212
|
+
const claudeRoot = join42(HOME2, ".claude", "projects");
|
|
9213
|
+
if (existsSync32(claudeRoot)) {
|
|
7360
9214
|
installs.push({
|
|
7361
9215
|
agent: "claude_code",
|
|
7362
9216
|
sessionRoot: claudeRoot,
|
|
7363
9217
|
encodeCwd: encodeCwdClaudeCode
|
|
7364
9218
|
});
|
|
7365
9219
|
}
|
|
7366
|
-
const codexRoot =
|
|
7367
|
-
if (
|
|
9220
|
+
const codexRoot = join42(HOME2, ".codex", "sessions");
|
|
9221
|
+
if (existsSync32(codexRoot)) {
|
|
7368
9222
|
installs.push({
|
|
7369
9223
|
agent: "codex",
|
|
7370
9224
|
sessionRoot: codexRoot,
|
|
@@ -7386,12 +9240,12 @@ function listLocalSessions(installs, cwd) {
|
|
|
7386
9240
|
const cwdEncoded = install.encodeCwd(cwd);
|
|
7387
9241
|
let subdirs = [];
|
|
7388
9242
|
try {
|
|
7389
|
-
subdirs =
|
|
9243
|
+
subdirs = readdirSync7(install.sessionRoot);
|
|
7390
9244
|
} catch {
|
|
7391
9245
|
continue;
|
|
7392
9246
|
}
|
|
7393
9247
|
for (const sub of subdirs) {
|
|
7394
|
-
const subdirPath =
|
|
9248
|
+
const subdirPath = join42(install.sessionRoot, sub);
|
|
7395
9249
|
try {
|
|
7396
9250
|
if (!statSync7(subdirPath).isDirectory())
|
|
7397
9251
|
continue;
|
|
@@ -7401,14 +9255,14 @@ function listLocalSessions(installs, cwd) {
|
|
|
7401
9255
|
const inCwd = sub === cwdEncoded;
|
|
7402
9256
|
let files = [];
|
|
7403
9257
|
try {
|
|
7404
|
-
files =
|
|
9258
|
+
files = readdirSync7(subdirPath);
|
|
7405
9259
|
} catch {
|
|
7406
9260
|
continue;
|
|
7407
9261
|
}
|
|
7408
9262
|
for (const f of files) {
|
|
7409
9263
|
if (!f.endsWith(".jsonl"))
|
|
7410
9264
|
continue;
|
|
7411
|
-
const fullPath =
|
|
9265
|
+
const fullPath = join42(subdirPath, f);
|
|
7412
9266
|
let stats;
|
|
7413
9267
|
try {
|
|
7414
9268
|
stats = statSync7(fullPath);
|
|
@@ -7469,7 +9323,7 @@ function pickSessions(candidates, opts) {
|
|
|
7469
9323
|
function nativeJsonlToRows(filePath, sessionId, agent) {
|
|
7470
9324
|
let raw;
|
|
7471
9325
|
try {
|
|
7472
|
-
raw =
|
|
9326
|
+
raw = readFileSync29(filePath, "utf-8");
|
|
7473
9327
|
} catch {
|
|
7474
9328
|
return [];
|
|
7475
9329
|
}
|
|
@@ -7559,22 +9413,22 @@ function extractPairs(rows) {
|
|
|
7559
9413
|
}
|
|
7560
9414
|
|
|
7561
9415
|
// dist/src/skillify/gate-runner.js
|
|
7562
|
-
import { existsSync as
|
|
9416
|
+
import { existsSync as existsSync33 } from "node:fs";
|
|
7563
9417
|
import { createRequire } from "node:module";
|
|
7564
|
-
import { homedir as
|
|
7565
|
-
import { join as
|
|
9418
|
+
import { homedir as homedir21 } from "node:os";
|
|
9419
|
+
import { join as join43 } from "node:path";
|
|
7566
9420
|
var requireForCp = createRequire(import.meta.url);
|
|
7567
9421
|
var { execFileSync: runChildProcess } = requireForCp("node:child_process");
|
|
7568
9422
|
var inheritedEnv = process;
|
|
7569
9423
|
function firstExistingPath(candidates) {
|
|
7570
9424
|
for (const c of candidates) {
|
|
7571
|
-
if (
|
|
9425
|
+
if (existsSync33(c))
|
|
7572
9426
|
return c;
|
|
7573
9427
|
}
|
|
7574
9428
|
return null;
|
|
7575
9429
|
}
|
|
7576
9430
|
function findAgentBin(agent) {
|
|
7577
|
-
const home =
|
|
9431
|
+
const home = homedir21();
|
|
7578
9432
|
switch (agent) {
|
|
7579
9433
|
// /usr/bin/<name> is included in every candidate list — that's the
|
|
7580
9434
|
// common Linux package-manager install path (apt, dnf, pacman). Old
|
|
@@ -7583,45 +9437,45 @@ function findAgentBin(agent) {
|
|
|
7583
9437
|
// #170 caught the gap.
|
|
7584
9438
|
case "claude_code":
|
|
7585
9439
|
return firstExistingPath([
|
|
7586
|
-
|
|
9440
|
+
join43(home, ".claude", "local", "claude"),
|
|
7587
9441
|
"/usr/local/bin/claude",
|
|
7588
9442
|
"/usr/bin/claude",
|
|
7589
|
-
|
|
7590
|
-
|
|
9443
|
+
join43(home, ".npm-global", "bin", "claude"),
|
|
9444
|
+
join43(home, ".local", "bin", "claude"),
|
|
7591
9445
|
"/opt/homebrew/bin/claude"
|
|
7592
|
-
]) ??
|
|
9446
|
+
]) ?? join43(home, ".claude", "local", "claude");
|
|
7593
9447
|
case "codex":
|
|
7594
9448
|
return firstExistingPath([
|
|
7595
9449
|
"/usr/local/bin/codex",
|
|
7596
9450
|
"/usr/bin/codex",
|
|
7597
|
-
|
|
7598
|
-
|
|
9451
|
+
join43(home, ".npm-global", "bin", "codex"),
|
|
9452
|
+
join43(home, ".local", "bin", "codex"),
|
|
7599
9453
|
"/opt/homebrew/bin/codex"
|
|
7600
9454
|
]) ?? "/usr/local/bin/codex";
|
|
7601
9455
|
case "cursor":
|
|
7602
9456
|
return firstExistingPath([
|
|
7603
9457
|
"/usr/local/bin/cursor-agent",
|
|
7604
9458
|
"/usr/bin/cursor-agent",
|
|
7605
|
-
|
|
7606
|
-
|
|
9459
|
+
join43(home, ".npm-global", "bin", "cursor-agent"),
|
|
9460
|
+
join43(home, ".local", "bin", "cursor-agent"),
|
|
7607
9461
|
"/opt/homebrew/bin/cursor-agent"
|
|
7608
9462
|
]) ?? "/usr/local/bin/cursor-agent";
|
|
7609
9463
|
case "hermes":
|
|
7610
9464
|
return firstExistingPath([
|
|
7611
|
-
|
|
9465
|
+
join43(home, ".local", "bin", "hermes"),
|
|
7612
9466
|
"/usr/local/bin/hermes",
|
|
7613
9467
|
"/usr/bin/hermes",
|
|
7614
|
-
|
|
9468
|
+
join43(home, ".npm-global", "bin", "hermes"),
|
|
7615
9469
|
"/opt/homebrew/bin/hermes"
|
|
7616
|
-
]) ??
|
|
9470
|
+
]) ?? join43(home, ".local", "bin", "hermes");
|
|
7617
9471
|
case "pi":
|
|
7618
9472
|
return firstExistingPath([
|
|
7619
|
-
|
|
9473
|
+
join43(home, ".local", "bin", "pi"),
|
|
7620
9474
|
"/usr/local/bin/pi",
|
|
7621
9475
|
"/usr/bin/pi",
|
|
7622
|
-
|
|
9476
|
+
join43(home, ".npm-global", "bin", "pi"),
|
|
7623
9477
|
"/opt/homebrew/bin/pi"
|
|
7624
|
-
]) ??
|
|
9478
|
+
]) ?? join43(home, ".local", "bin", "pi");
|
|
7625
9479
|
}
|
|
7626
9480
|
}
|
|
7627
9481
|
|
|
@@ -7651,28 +9505,28 @@ function extractJsonBlock(s) {
|
|
|
7651
9505
|
}
|
|
7652
9506
|
|
|
7653
9507
|
// dist/src/skillify/local-manifest.js
|
|
7654
|
-
import { existsSync as
|
|
7655
|
-
import { homedir as
|
|
7656
|
-
import { dirname as
|
|
7657
|
-
var LOCAL_MANIFEST_PATH =
|
|
7658
|
-
var LOCAL_MINE_LOCK_PATH =
|
|
9508
|
+
import { existsSync as existsSync34, mkdirSync as mkdirSync21, readFileSync as readFileSync30, writeFileSync as writeFileSync23 } from "node:fs";
|
|
9509
|
+
import { homedir as homedir22 } from "node:os";
|
|
9510
|
+
import { dirname as dirname15, join as join44 } from "node:path";
|
|
9511
|
+
var LOCAL_MANIFEST_PATH = join44(homedir22(), ".claude", "hivemind", "local-mined.json");
|
|
9512
|
+
var LOCAL_MINE_LOCK_PATH = join44(homedir22(), ".claude", "hivemind", "local-mined.lock");
|
|
7659
9513
|
function readLocalManifest(path = LOCAL_MANIFEST_PATH) {
|
|
7660
|
-
if (!
|
|
9514
|
+
if (!existsSync34(path))
|
|
7661
9515
|
return null;
|
|
7662
9516
|
try {
|
|
7663
|
-
return JSON.parse(
|
|
9517
|
+
return JSON.parse(readFileSync30(path, "utf-8"));
|
|
7664
9518
|
} catch {
|
|
7665
9519
|
return null;
|
|
7666
9520
|
}
|
|
7667
9521
|
}
|
|
7668
9522
|
function writeLocalManifest(m, path = LOCAL_MANIFEST_PATH) {
|
|
7669
|
-
|
|
7670
|
-
|
|
9523
|
+
mkdirSync21(dirname15(path), { recursive: true });
|
|
9524
|
+
writeFileSync23(path, JSON.stringify(m, null, 2));
|
|
7671
9525
|
}
|
|
7672
9526
|
var LATEST_RUN_WINDOW_MS = 5 * 60 * 1e3;
|
|
7673
9527
|
|
|
7674
9528
|
// dist/src/commands/mine-local.js
|
|
7675
|
-
import { unlinkSync as
|
|
9529
|
+
import { unlinkSync as unlinkSync12 } from "node:fs";
|
|
7676
9530
|
var EPSILON = 0.3;
|
|
7677
9531
|
var DEFAULT_N = 8;
|
|
7678
9532
|
var PAIR_CHAR_CAP = 4e3;
|
|
@@ -7683,9 +9537,9 @@ var IN_FLIGHT_MAX_AGE_MS = 6e4;
|
|
|
7683
9537
|
var GATE_TIMEOUT_MS = 24e4;
|
|
7684
9538
|
var MANIFEST_PATH = LOCAL_MANIFEST_PATH;
|
|
7685
9539
|
function runGateViaStdin(opts) {
|
|
7686
|
-
return new Promise((
|
|
9540
|
+
return new Promise((resolve6) => {
|
|
7687
9541
|
if (opts.agent !== "claude_code") {
|
|
7688
|
-
|
|
9542
|
+
resolve6({
|
|
7689
9543
|
stdout: "",
|
|
7690
9544
|
stderr: "",
|
|
7691
9545
|
errored: true,
|
|
@@ -7693,8 +9547,8 @@ function runGateViaStdin(opts) {
|
|
|
7693
9547
|
});
|
|
7694
9548
|
return;
|
|
7695
9549
|
}
|
|
7696
|
-
if (!
|
|
7697
|
-
|
|
9550
|
+
if (!existsSync35(opts.bin)) {
|
|
9551
|
+
resolve6({
|
|
7698
9552
|
stdout: "",
|
|
7699
9553
|
stderr: "",
|
|
7700
9554
|
errored: true,
|
|
@@ -7721,7 +9575,7 @@ function runGateViaStdin(opts) {
|
|
|
7721
9575
|
if (settled)
|
|
7722
9576
|
return;
|
|
7723
9577
|
settled = true;
|
|
7724
|
-
|
|
9578
|
+
resolve6(r);
|
|
7725
9579
|
};
|
|
7726
9580
|
const timer = setTimeout(() => {
|
|
7727
9581
|
try {
|
|
@@ -8000,7 +9854,7 @@ async function runMineLocal(args) {
|
|
|
8000
9854
|
return;
|
|
8001
9855
|
lockReleased = true;
|
|
8002
9856
|
try {
|
|
8003
|
-
|
|
9857
|
+
unlinkSync12(LOCAL_MINE_LOCK_PATH);
|
|
8004
9858
|
} catch {
|
|
8005
9859
|
}
|
|
8006
9860
|
};
|
|
@@ -8057,8 +9911,8 @@ async function runMineLocalImpl(args) {
|
|
|
8057
9911
|
console.log(`Dry-run: would invoke ${gateAgent} gate on ${picked.length} session(s) in parallel (concurrency=${GATE_CONCURRENCY}).`);
|
|
8058
9912
|
return;
|
|
8059
9913
|
}
|
|
8060
|
-
const tmpDir =
|
|
8061
|
-
|
|
9914
|
+
const tmpDir = join45(homedir23(), ".claude", "hivemind", `mine-local-${Date.now()}`);
|
|
9915
|
+
mkdirSync22(tmpDir, { recursive: true });
|
|
8062
9916
|
console.log(`Running ${picked.length} gate call(s) in parallel (concurrency=${GATE_CONCURRENCY}, timeout=${GATE_TIMEOUT_MS / 1e3}s each)...`);
|
|
8063
9917
|
const results = await parallelMap(picked, GATE_CONCURRENCY, async (s) => {
|
|
8064
9918
|
const shortId = s.sessionId.slice(0, 8);
|
|
@@ -8069,23 +9923,23 @@ async function runMineLocalImpl(args) {
|
|
|
8069
9923
|
return { session: s, skills: [], reason: "no pairs", error: null };
|
|
8070
9924
|
}
|
|
8071
9925
|
const tail = pairs2.slice(-PER_SESSION_PAIR_CAP);
|
|
8072
|
-
const sessionTmp =
|
|
8073
|
-
|
|
8074
|
-
const verdictPath =
|
|
9926
|
+
const sessionTmp = join45(tmpDir, `s-${shortId}`);
|
|
9927
|
+
mkdirSync22(sessionTmp, { recursive: true });
|
|
9928
|
+
const verdictPath = join45(sessionTmp, "verdict.json");
|
|
8075
9929
|
const prompt = buildSessionPrompt(tail, s, verdictPath);
|
|
8076
|
-
|
|
9930
|
+
writeFileSync24(join45(sessionTmp, "prompt.txt"), prompt);
|
|
8077
9931
|
const gate = await runGateViaStdin({ agent: gateAgent, bin: gateBin, prompt, timeoutMs: GATE_TIMEOUT_MS });
|
|
8078
9932
|
try {
|
|
8079
|
-
|
|
9933
|
+
writeFileSync24(join45(sessionTmp, "gate-stdout.txt"), gate.stdout);
|
|
8080
9934
|
if (gate.stderr)
|
|
8081
|
-
|
|
9935
|
+
writeFileSync24(join45(sessionTmp, "gate-stderr.txt"), gate.stderr);
|
|
8082
9936
|
} catch {
|
|
8083
9937
|
}
|
|
8084
9938
|
if (gate.errored) {
|
|
8085
9939
|
console.log(` [${shortId}] gate failed: ${gate.errorMessage}`);
|
|
8086
9940
|
return { session: s, skills: [], reason: null, error: gate.errorMessage ?? "gate failed" };
|
|
8087
9941
|
}
|
|
8088
|
-
const verdictText =
|
|
9942
|
+
const verdictText = existsSync35(verdictPath) ? readFileSync31(verdictPath, "utf-8") : gate.stdout;
|
|
8089
9943
|
const mv = parseMultiVerdict(verdictText);
|
|
8090
9944
|
if (!mv) {
|
|
8091
9945
|
console.log(` [${shortId}] unparseable verdict (kept at ${sessionTmp})`);
|
|
@@ -8137,7 +9991,7 @@ async function runMineLocalImpl(args) {
|
|
|
8137
9991
|
sourceSessions: [session.sessionId],
|
|
8138
9992
|
agent: gateAgent
|
|
8139
9993
|
});
|
|
8140
|
-
const canonicalDir =
|
|
9994
|
+
const canonicalDir = dirname16(result.path);
|
|
8141
9995
|
const symlinks = fanOutRoots.length > 0 ? fanOutSymlinks(canonicalDir, basename2(canonicalDir), fanOutRoots) : [];
|
|
8142
9996
|
const symlinkSuffix = symlinks.length > 0 ? `, fan-out \u2192 ${symlinks.length} root(s)` : "";
|
|
8143
9997
|
console.log(` wrote ${skill.name} \u2190 session ${session.sessionId.slice(0, 8)} (${session.agent}${symlinkSuffix})`);
|
|
@@ -8314,11 +10168,11 @@ function showStatus() {
|
|
|
8314
10168
|
console.log(`team: ${cfg.team.length === 0 ? "(empty)" : cfg.team.join(", ")}`);
|
|
8315
10169
|
console.log(`install: ${cfg.install} (${cfg.install === "global" ? "~/.claude/skills/" : "<project>/.claude/skills/"})`);
|
|
8316
10170
|
const dir = stateDir();
|
|
8317
|
-
if (!
|
|
10171
|
+
if (!existsSync36(dir)) {
|
|
8318
10172
|
console.log(`state: (no projects tracked yet)`);
|
|
8319
10173
|
return;
|
|
8320
10174
|
}
|
|
8321
|
-
const files =
|
|
10175
|
+
const files = readdirSync8(dir).filter((f) => f.endsWith(".json") && f !== "config.json" && f !== "pulled.json" && f !== "autopull-last-run.json");
|
|
8322
10176
|
if (files.length === 0) {
|
|
8323
10177
|
console.log(`state: (no projects tracked yet)`);
|
|
8324
10178
|
return;
|
|
@@ -8326,7 +10180,7 @@ function showStatus() {
|
|
|
8326
10180
|
console.log(`state: ${files.length} project(s) tracked`);
|
|
8327
10181
|
for (const f of files) {
|
|
8328
10182
|
try {
|
|
8329
|
-
const s = JSON.parse(
|
|
10183
|
+
const s = JSON.parse(readFileSync32(join46(dir, f), "utf-8"));
|
|
8330
10184
|
const last = typeof s.updatedAt === "number" ? new Date(s.updatedAt).toISOString() : s.lastDate ?? "never";
|
|
8331
10185
|
const skills = Array.isArray(s.skillsGenerated) && s.skillsGenerated.length > 0 ? s.skillsGenerated.join(", ") : "none";
|
|
8332
10186
|
console.log(` - ${s.project} (counter=${s.counter}, last=${last}, skills=${skills})`);
|
|
@@ -8353,7 +10207,7 @@ function setInstall(loc) {
|
|
|
8353
10207
|
}
|
|
8354
10208
|
const cfg = loadScopeConfig();
|
|
8355
10209
|
saveScopeConfig({ ...cfg, install: loc });
|
|
8356
|
-
const path = loc === "global" ?
|
|
10210
|
+
const path = loc === "global" ? join46(homedir24(), ".claude", "skills") : "<cwd>/.claude/skills";
|
|
8357
10211
|
console.log(`Install location set to '${loc}'. New skills will be written to ${path}/<name>/SKILL.md.`);
|
|
8358
10212
|
}
|
|
8359
10213
|
function promoteSkill(name, cwd) {
|
|
@@ -8361,18 +10215,18 @@ function promoteSkill(name, cwd) {
|
|
|
8361
10215
|
console.error("Usage: hivemind skillify promote <skill-name>");
|
|
8362
10216
|
process.exit(1);
|
|
8363
10217
|
}
|
|
8364
|
-
const projectPath =
|
|
8365
|
-
const globalPath =
|
|
8366
|
-
if (!
|
|
10218
|
+
const projectPath = join46(cwd, ".claude", "skills", name);
|
|
10219
|
+
const globalPath = join46(homedir24(), ".claude", "skills", name);
|
|
10220
|
+
if (!existsSync36(join46(projectPath, "SKILL.md"))) {
|
|
8367
10221
|
console.error(`Skill '${name}' not found at ${projectPath}/SKILL.md`);
|
|
8368
10222
|
process.exit(1);
|
|
8369
10223
|
}
|
|
8370
|
-
if (
|
|
10224
|
+
if (existsSync36(join46(globalPath, "SKILL.md"))) {
|
|
8371
10225
|
console.error(`Skill '${name}' already exists at ${globalPath}/SKILL.md \u2014 refusing to overwrite. Remove it first or rename the project skill.`);
|
|
8372
10226
|
process.exit(1);
|
|
8373
10227
|
}
|
|
8374
|
-
|
|
8375
|
-
|
|
10228
|
+
mkdirSync23(dirname17(globalPath), { recursive: true });
|
|
10229
|
+
renameSync12(projectPath, globalPath);
|
|
8376
10230
|
console.log(`Promoted '${name}' from ${projectPath} \u2192 ${globalPath}.`);
|
|
8377
10231
|
}
|
|
8378
10232
|
function teamAdd(name) {
|
|
@@ -8478,7 +10332,7 @@ async function pullSkills(args) {
|
|
|
8478
10332
|
console.error(`pull failed: ${e?.message ?? e}`);
|
|
8479
10333
|
process.exit(1);
|
|
8480
10334
|
}
|
|
8481
|
-
const dest = toRaw === "global" ?
|
|
10335
|
+
const dest = toRaw === "global" ? join46(homedir24(), ".claude", "skills") : `${process.cwd()}/.claude/skills`;
|
|
8482
10336
|
const filterDesc = users.length === 0 ? "all users" : users.join(", ");
|
|
8483
10337
|
console.log(`Destination: ${dest}`);
|
|
8484
10338
|
console.log(`Filter: ${filterDesc}${skillName ? ` \xB7 skill='${skillName}'` : ""}${dryRun ? " \xB7 dry-run" : ""}${force ? " \xB7 force" : ""}`);
|
|
@@ -8528,7 +10382,7 @@ async function unpullSkills(args) {
|
|
|
8528
10382
|
all,
|
|
8529
10383
|
legacyCleanup
|
|
8530
10384
|
});
|
|
8531
|
-
const dest = toRaw === "global" ?
|
|
10385
|
+
const dest = toRaw === "global" ? join46(homedir24(), ".claude", "skills") : `${process.cwd()}/.claude/skills`;
|
|
8532
10386
|
const filterParts = [];
|
|
8533
10387
|
if (users.length > 0)
|
|
8534
10388
|
filterParts.push(`users=${users.join(",")}`);
|
|
@@ -8623,15 +10477,15 @@ if (process.argv[1] && process.argv[1].endsWith("skillify.js")) {
|
|
|
8623
10477
|
}
|
|
8624
10478
|
|
|
8625
10479
|
// dist/src/cli/update.js
|
|
8626
|
-
import { execFileSync as
|
|
8627
|
-
import { closeSync as closeSync3, existsSync as
|
|
8628
|
-
import { homedir as
|
|
8629
|
-
import { dirname as
|
|
10480
|
+
import { execFileSync as execFileSync6 } from "node:child_process";
|
|
10481
|
+
import { closeSync as closeSync3, existsSync as existsSync37, mkdirSync as mkdirSync24, openSync as openSync3, readFileSync as readFileSync34, realpathSync, unlinkSync as unlinkSync13, writeSync as writeSync2 } from "node:fs";
|
|
10482
|
+
import { homedir as homedir25 } from "node:os";
|
|
10483
|
+
import { dirname as dirname19, join as join48, sep as sep2 } from "node:path";
|
|
8630
10484
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
8631
10485
|
|
|
8632
10486
|
// dist/src/utils/version-check.js
|
|
8633
|
-
import { readFileSync as
|
|
8634
|
-
import { dirname as
|
|
10487
|
+
import { readFileSync as readFileSync33 } from "node:fs";
|
|
10488
|
+
import { dirname as dirname18, join as join47 } from "node:path";
|
|
8635
10489
|
function isNewer(latest, current) {
|
|
8636
10490
|
const parse = (v) => v.split(".").map(Number);
|
|
8637
10491
|
const [la, lb, lc] = parse(latest);
|
|
@@ -8643,7 +10497,7 @@ function isNewer(latest, current) {
|
|
|
8643
10497
|
var NPM_REGISTRY_URL = "https://registry.npmjs.org/@deeplake/hivemind/latest";
|
|
8644
10498
|
var PKG_NAME = "@deeplake/hivemind";
|
|
8645
10499
|
function defaultLockPath() {
|
|
8646
|
-
return
|
|
10500
|
+
return join48(homedir25(), ".deeplake", "hivemind-update.lock");
|
|
8647
10501
|
}
|
|
8648
10502
|
function detectInstallKind(argv1) {
|
|
8649
10503
|
const realArgv1 = (() => {
|
|
@@ -8653,36 +10507,36 @@ function detectInstallKind(argv1) {
|
|
|
8653
10507
|
return argv1 ?? process.argv[1] ?? fileURLToPath2(import.meta.url);
|
|
8654
10508
|
}
|
|
8655
10509
|
})();
|
|
8656
|
-
let dir =
|
|
10510
|
+
let dir = dirname19(realArgv1);
|
|
8657
10511
|
let installDir = null;
|
|
8658
10512
|
for (let i = 0; i < 10; i++) {
|
|
8659
|
-
const pkgPath = `${dir}${
|
|
10513
|
+
const pkgPath = `${dir}${sep2}package.json`;
|
|
8660
10514
|
try {
|
|
8661
|
-
const pkg = JSON.parse(
|
|
10515
|
+
const pkg = JSON.parse(readFileSync34(pkgPath, "utf-8"));
|
|
8662
10516
|
if (pkg.name === PKG_NAME || pkg.name === "hivemind") {
|
|
8663
10517
|
installDir = dir;
|
|
8664
10518
|
break;
|
|
8665
10519
|
}
|
|
8666
10520
|
} catch {
|
|
8667
10521
|
}
|
|
8668
|
-
const parent =
|
|
10522
|
+
const parent = dirname19(dir);
|
|
8669
10523
|
if (parent === dir)
|
|
8670
10524
|
break;
|
|
8671
10525
|
dir = parent;
|
|
8672
10526
|
}
|
|
8673
|
-
installDir ??=
|
|
8674
|
-
if (realArgv1.includes(`${
|
|
10527
|
+
installDir ??= dirname19(realArgv1);
|
|
10528
|
+
if (realArgv1.includes(`${sep2}_npx${sep2}`) || realArgv1.includes(`${sep2}.npx${sep2}`)) {
|
|
8675
10529
|
return { kind: "npx", installDir };
|
|
8676
10530
|
}
|
|
8677
|
-
if (realArgv1.includes(`${
|
|
10531
|
+
if (realArgv1.includes(`${sep2}node_modules${sep2}@deeplake${sep2}hivemind`) || realArgv1.includes(`${sep2}node_modules${sep2}hivemind`)) {
|
|
8678
10532
|
return { kind: "npm-global", installDir };
|
|
8679
10533
|
}
|
|
8680
10534
|
let gitDir = installDir;
|
|
8681
10535
|
for (let i = 0; i < 6; i++) {
|
|
8682
|
-
if (
|
|
10536
|
+
if (existsSync37(`${gitDir}${sep2}.git`)) {
|
|
8683
10537
|
return { kind: "local-dev", installDir };
|
|
8684
10538
|
}
|
|
8685
|
-
const parent =
|
|
10539
|
+
const parent = dirname19(gitDir);
|
|
8686
10540
|
if (parent === gitDir)
|
|
8687
10541
|
break;
|
|
8688
10542
|
gitDir = parent;
|
|
@@ -8701,10 +10555,10 @@ async function getLatestNpmVersion(timeoutMs = 5e3) {
|
|
|
8701
10555
|
}
|
|
8702
10556
|
}
|
|
8703
10557
|
var defaultSpawn = (cmd, args) => {
|
|
8704
|
-
|
|
10558
|
+
execFileSync6(cmd, args, { stdio: "inherit" });
|
|
8705
10559
|
};
|
|
8706
10560
|
function tryAcquireLock(path) {
|
|
8707
|
-
|
|
10561
|
+
mkdirSync24(dirname19(path), { recursive: true, mode: 448 });
|
|
8708
10562
|
const claim = () => {
|
|
8709
10563
|
const fd = openSync3(path, "wx", 384);
|
|
8710
10564
|
writeSync2(fd, String(process.pid));
|
|
@@ -8718,7 +10572,7 @@ function tryAcquireLock(path) {
|
|
|
8718
10572
|
}
|
|
8719
10573
|
let holderPid = 0;
|
|
8720
10574
|
try {
|
|
8721
|
-
holderPid = Number(
|
|
10575
|
+
holderPid = Number(readFileSync34(path, "utf-8").trim()) || 0;
|
|
8722
10576
|
} catch {
|
|
8723
10577
|
try {
|
|
8724
10578
|
return claim();
|
|
@@ -8735,7 +10589,7 @@ function tryAcquireLock(path) {
|
|
|
8735
10589
|
}
|
|
8736
10590
|
}
|
|
8737
10591
|
try {
|
|
8738
|
-
|
|
10592
|
+
unlinkSync13(path);
|
|
8739
10593
|
} catch {
|
|
8740
10594
|
}
|
|
8741
10595
|
try {
|
|
@@ -8751,7 +10605,7 @@ function releaseLock(fd, path) {
|
|
|
8751
10605
|
} catch {
|
|
8752
10606
|
}
|
|
8753
10607
|
try {
|
|
8754
|
-
|
|
10608
|
+
unlinkSync13(path);
|
|
8755
10609
|
} catch {
|
|
8756
10610
|
}
|
|
8757
10611
|
}
|
|
@@ -8857,7 +10711,7 @@ var AUTH_SUBCOMMANDS = /* @__PURE__ */ new Set([
|
|
|
8857
10711
|
"autoupdate",
|
|
8858
10712
|
"sessions"
|
|
8859
10713
|
]);
|
|
8860
|
-
var
|
|
10714
|
+
var USAGE3 = `
|
|
8861
10715
|
hivemind \u2014 one brain for every agent on your team
|
|
8862
10716
|
|
|
8863
10717
|
Usage:
|
|
@@ -8927,6 +10781,22 @@ Semantic search (embeddings):
|
|
|
8927
10781
|
Add --with-embeddings to "hivemind install" (or "hivemind <agent> install")
|
|
8928
10782
|
to run "embeddings install" automatically after installing the agent(s).
|
|
8929
10783
|
|
|
10784
|
+
Codebase graph (per-repo AST snapshot + cloud sync):
|
|
10785
|
+
hivemind graph build [--cwd <path>] Walk TypeScript sources, extract
|
|
10786
|
+
AST nodes + edges, write a
|
|
10787
|
+
snapshot, and push to cloud.
|
|
10788
|
+
hivemind graph diff <sha1> <sha2> Diff two snapshots by commit.
|
|
10789
|
+
hivemind graph history [-n N] [--json] Show last N build entries.
|
|
10790
|
+
hivemind graph init [--force] Install a managed
|
|
10791
|
+
.git/hooks/post-commit hook
|
|
10792
|
+
that rebuilds on each commit.
|
|
10793
|
+
hivemind graph pull Download the freshest cloud
|
|
10794
|
+
snapshot for HEAD into local.
|
|
10795
|
+
hivemind graph uninstall Remove the managed post-commit
|
|
10796
|
+
hook.
|
|
10797
|
+
Agents query the local snapshot via the Deeplake mount at
|
|
10798
|
+
~/.deeplake/memory/graph/{index.md,find/<pattern>,show/<handle-or-pattern>}.
|
|
10799
|
+
|
|
8930
10800
|
Skill management (mine + share reusable Claude skills across the org):
|
|
8931
10801
|
${renderCliHelpBlock()}
|
|
8932
10802
|
|
|
@@ -9117,7 +10987,7 @@ async function main() {
|
|
|
9117
10987
|
const args = process.argv.slice(2);
|
|
9118
10988
|
const cmd = args[0];
|
|
9119
10989
|
if (!cmd || cmd === "--help" || cmd === "-h" || cmd === "help") {
|
|
9120
|
-
log(
|
|
10990
|
+
log(USAGE3);
|
|
9121
10991
|
return;
|
|
9122
10992
|
}
|
|
9123
10993
|
if (cmd === "--version" || cmd === "-v" || cmd === "version") {
|
|
@@ -9151,6 +11021,10 @@ async function main() {
|
|
|
9151
11021
|
runSkillifyCommand(args.slice(1));
|
|
9152
11022
|
return;
|
|
9153
11023
|
}
|
|
11024
|
+
if (cmd === "graph") {
|
|
11025
|
+
await runGraphCommand(args.slice(1));
|
|
11026
|
+
return;
|
|
11027
|
+
}
|
|
9154
11028
|
if (cmd === "dashboard") {
|
|
9155
11029
|
const code = await runDashboardCommand(args.slice(1));
|
|
9156
11030
|
process.exit(code);
|
|
@@ -9202,7 +11076,7 @@ async function main() {
|
|
|
9202
11076
|
return;
|
|
9203
11077
|
}
|
|
9204
11078
|
warn(`Unknown command: ${cmd}`);
|
|
9205
|
-
log(
|
|
11079
|
+
log(USAGE3);
|
|
9206
11080
|
process.exit(1);
|
|
9207
11081
|
}
|
|
9208
11082
|
main().catch((err) => {
|