@deeplake/hivemind 0.7.45 → 0.7.47
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/README.md +64 -0
- package/bundle/cli.js +22702 -7775
- package/codex/bundle/capture.js +228 -0
- package/codex/bundle/commands/auth-login.js +228 -0
- package/codex/bundle/graph-pull-worker.js +1370 -0
- package/codex/bundle/pre-tool-use.js +228 -0
- package/codex/bundle/session-start-setup.js +228 -0
- package/codex/bundle/session-start.js +255 -4
- package/codex/bundle/shell/deeplake-shell.js +1028 -28
- package/codex/bundle/skillify-worker.js +94 -3
- package/codex/bundle/stop.js +282 -50
- package/codex/skills/hivemind-goals/SKILL.md +157 -0
- package/cursor/bundle/capture.js +282 -50
- package/cursor/bundle/commands/auth-login.js +228 -0
- package/cursor/bundle/graph-pull-worker.js +1370 -0
- package/cursor/bundle/pre-tool-use.js +228 -0
- package/cursor/bundle/session-end.js +65 -44
- package/cursor/bundle/session-start.js +662 -6
- package/cursor/bundle/shell/deeplake-shell.js +1028 -28
- package/cursor/bundle/skillify-worker.js +94 -3
- package/hermes/bundle/capture.js +282 -50
- package/hermes/bundle/commands/auth-login.js +228 -0
- package/hermes/bundle/graph-pull-worker.js +1370 -0
- package/hermes/bundle/pre-tool-use.js +228 -0
- package/hermes/bundle/session-end.js +65 -44
- package/hermes/bundle/session-start.js +662 -6
- package/hermes/bundle/shell/deeplake-shell.js +1028 -28
- package/hermes/bundle/skillify-worker.js +94 -3
- package/mcp/bundle/server.js +228 -0
- package/openclaw/dist/chunks/config-FH6JYSJW.js +53 -0
- package/openclaw/dist/index.js +307 -2
- package/openclaw/dist/skillify-worker.js +94 -3
- package/openclaw/openclaw.plugin.json +4 -2
- package/openclaw/package.json +1 -1
- package/openclaw/skills/hivemind-goals/SKILL.md +30 -0
- package/package.json +4 -1
- package/openclaw/dist/chunks/config-XEK4MJJS.js +0 -36
|
@@ -46081,14 +46081,14 @@ var require_turndown_cjs = __commonJS({
|
|
|
46081
46081
|
} else if (node.nodeType === 1) {
|
|
46082
46082
|
replacement = replacementForNode.call(self2, node);
|
|
46083
46083
|
}
|
|
46084
|
-
return
|
|
46084
|
+
return join18(output, replacement);
|
|
46085
46085
|
}, "");
|
|
46086
46086
|
}
|
|
46087
46087
|
function postProcess(output) {
|
|
46088
46088
|
var self2 = this;
|
|
46089
46089
|
this.rules.forEach(function(rule) {
|
|
46090
46090
|
if (typeof rule.append === "function") {
|
|
46091
|
-
output =
|
|
46091
|
+
output = join18(output, rule.append(self2.options));
|
|
46092
46092
|
}
|
|
46093
46093
|
});
|
|
46094
46094
|
return output.replace(/^[\t\r\n]+/, "").replace(/[\t\r\n\s]+$/, "");
|
|
@@ -46100,7 +46100,7 @@ var require_turndown_cjs = __commonJS({
|
|
|
46100
46100
|
if (whitespace.leading || whitespace.trailing) content = content.trim();
|
|
46101
46101
|
return whitespace.leading + rule.replacement(content, node, this.options) + whitespace.trailing;
|
|
46102
46102
|
}
|
|
46103
|
-
function
|
|
46103
|
+
function join18(output, replacement) {
|
|
46104
46104
|
var s12 = trimTrailingNewlines(output);
|
|
46105
46105
|
var s22 = trimLeadingNewlines(replacement);
|
|
46106
46106
|
var nls = Math.max(output.length - s12.length, replacement.length - s22.length);
|
|
@@ -66792,6 +66792,23 @@ function loadConfig() {
|
|
|
66792
66792
|
tableName: process.env.HIVEMIND_TABLE ?? "memory",
|
|
66793
66793
|
sessionsTableName: process.env.HIVEMIND_SESSIONS_TABLE ?? "sessions",
|
|
66794
66794
|
skillsTableName: process.env.HIVEMIND_SKILLS_TABLE ?? "skills",
|
|
66795
|
+
// Defaults match the table name written into the SQL — keep aligned
|
|
66796
|
+
// with RULES_COLUMNS / TASKS_COLUMNS / TASK_EVENTS_COLUMNS in
|
|
66797
|
+
// deeplake-schema.ts and with the e2e test-org override convention
|
|
66798
|
+
// (memory_test / sessions_test → goals_test, etc.) documented in
|
|
66799
|
+
// CLAUDE.md.
|
|
66800
|
+
rulesTableName: process.env.HIVEMIND_RULES_TABLE ?? "hivemind_rules",
|
|
66801
|
+
tasksTableName: process.env.HIVEMIND_TASKS_TABLE ?? "hivemind_tasks",
|
|
66802
|
+
taskEventsTableName: process.env.HIVEMIND_TASK_EVENTS_TABLE ?? "hivemind_task_events",
|
|
66803
|
+
// Goals + KPIs (refined design — VFS path classifier maps
|
|
66804
|
+
// memory/goal/<user>/<status>/<uuid>.md → hivemind_goals row
|
|
66805
|
+
// memory/kpi/<uuid>/<kpi_id>.md → hivemind_kpis row
|
|
66806
|
+
// See src/shell/deeplake-fs.ts for the translation logic and
|
|
66807
|
+
// GOALS_COLUMNS / KPIS_COLUMNS in deeplake-schema.ts for the
|
|
66808
|
+
// table shape.
|
|
66809
|
+
goalsTableName: process.env.HIVEMIND_GOALS_TABLE ?? "hivemind_goals",
|
|
66810
|
+
kpisTableName: process.env.HIVEMIND_KPIS_TABLE ?? "hivemind_kpis",
|
|
66811
|
+
codebaseTableName: process.env.HIVEMIND_CODEBASE_TABLE ?? "codebase",
|
|
66795
66812
|
memoryPath: process.env.HIVEMIND_MEMORY_PATH ?? join4(home, ".deeplake", "memory")
|
|
66796
66813
|
};
|
|
66797
66814
|
}
|
|
@@ -66892,6 +66909,65 @@ var SKILLS_COLUMNS = Object.freeze([
|
|
|
66892
66909
|
{ name: "created_at", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
66893
66910
|
{ name: "updated_at", sql: "TEXT NOT NULL DEFAULT ''" }
|
|
66894
66911
|
]);
|
|
66912
|
+
var RULES_COLUMNS = Object.freeze([
|
|
66913
|
+
{ name: "id", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
66914
|
+
{ name: "rule_id", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
66915
|
+
{ name: "text", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
66916
|
+
{ name: "scope", sql: "TEXT NOT NULL DEFAULT 'team'" },
|
|
66917
|
+
{ name: "status", sql: "TEXT NOT NULL DEFAULT 'active'" },
|
|
66918
|
+
{ name: "assigned_by", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
66919
|
+
{ name: "version", sql: "BIGINT NOT NULL DEFAULT 1" },
|
|
66920
|
+
{ name: "created_at", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
66921
|
+
{ name: "agent", sql: "TEXT NOT NULL DEFAULT 'manual'" },
|
|
66922
|
+
{ name: "plugin_version", sql: "TEXT NOT NULL DEFAULT ''" }
|
|
66923
|
+
]);
|
|
66924
|
+
var TASKS_COLUMNS = Object.freeze([
|
|
66925
|
+
{ name: "id", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
66926
|
+
{ name: "task_id", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
66927
|
+
{ name: "text", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
66928
|
+
{ name: "scope", sql: "TEXT NOT NULL DEFAULT 'me'" },
|
|
66929
|
+
{ name: "status", sql: "TEXT NOT NULL DEFAULT 'active'" },
|
|
66930
|
+
{ name: "assigned_to", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
66931
|
+
{ name: "assigned_by", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
66932
|
+
{ name: "kpis", sql: "JSONB" },
|
|
66933
|
+
{ name: "version", sql: "BIGINT NOT NULL DEFAULT 1" },
|
|
66934
|
+
{ name: "created_at", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
66935
|
+
{ name: "agent", sql: "TEXT NOT NULL DEFAULT 'manual'" },
|
|
66936
|
+
{ name: "plugin_version", sql: "TEXT NOT NULL DEFAULT ''" }
|
|
66937
|
+
]);
|
|
66938
|
+
var TASK_EVENTS_COLUMNS = Object.freeze([
|
|
66939
|
+
{ name: "id", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
66940
|
+
{ name: "task_id", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
66941
|
+
{ name: "task_version", sql: "BIGINT NOT NULL DEFAULT 1" },
|
|
66942
|
+
{ name: "kpi_id", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
66943
|
+
{ name: "value", sql: "BIGINT NOT NULL DEFAULT 0" },
|
|
66944
|
+
{ name: "note", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
66945
|
+
{ name: "source", sql: "TEXT NOT NULL DEFAULT 'user'" },
|
|
66946
|
+
{ name: "agent", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
66947
|
+
{ name: "ts", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
66948
|
+
{ name: "plugin_version", sql: "TEXT NOT NULL DEFAULT ''" }
|
|
66949
|
+
]);
|
|
66950
|
+
var GOALS_COLUMNS = Object.freeze([
|
|
66951
|
+
{ name: "id", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
66952
|
+
{ name: "goal_id", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
66953
|
+
{ name: "owner", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
66954
|
+
{ name: "status", sql: "TEXT NOT NULL DEFAULT 'opened'" },
|
|
66955
|
+
{ name: "content", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
66956
|
+
{ name: "version", sql: "BIGINT NOT NULL DEFAULT 1" },
|
|
66957
|
+
{ name: "created_at", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
66958
|
+
{ name: "agent", sql: "TEXT NOT NULL DEFAULT 'manual'" },
|
|
66959
|
+
{ name: "plugin_version", sql: "TEXT NOT NULL DEFAULT ''" }
|
|
66960
|
+
]);
|
|
66961
|
+
var KPIS_COLUMNS = Object.freeze([
|
|
66962
|
+
{ name: "id", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
66963
|
+
{ name: "goal_id", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
66964
|
+
{ name: "kpi_id", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
66965
|
+
{ name: "content", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
66966
|
+
{ name: "version", sql: "BIGINT NOT NULL DEFAULT 1" },
|
|
66967
|
+
{ name: "created_at", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
66968
|
+
{ name: "agent", sql: "TEXT NOT NULL DEFAULT 'manual'" },
|
|
66969
|
+
{ name: "plugin_version", sql: "TEXT NOT NULL DEFAULT ''" }
|
|
66970
|
+
]);
|
|
66895
66971
|
function validateSchema(label, cols) {
|
|
66896
66972
|
const seen = /* @__PURE__ */ new Set();
|
|
66897
66973
|
for (const col of cols) {
|
|
@@ -66909,9 +66985,38 @@ function validateSchema(label, cols) {
|
|
|
66909
66985
|
}
|
|
66910
66986
|
}
|
|
66911
66987
|
}
|
|
66988
|
+
var CODEBASE_COLUMNS = Object.freeze([
|
|
66989
|
+
// Identity key (matches the PK below)
|
|
66990
|
+
{ name: "org_id", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
66991
|
+
{ name: "workspace_id", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
66992
|
+
{ name: "repo_slug", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
66993
|
+
{ name: "user_id", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
66994
|
+
{ name: "worktree_id", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
66995
|
+
{ name: "commit_sha", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
66996
|
+
// Observation metadata
|
|
66997
|
+
{ name: "parent_sha", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
66998
|
+
{ name: "branch", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
66999
|
+
{ name: "ts", sql: "TIMESTAMP" },
|
|
67000
|
+
{ name: "pushed_by", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
67001
|
+
// Snapshot payload
|
|
67002
|
+
{ name: "snapshot_sha256", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
67003
|
+
{ name: "snapshot_jsonb", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
67004
|
+
{ name: "node_count", sql: "BIGINT NOT NULL DEFAULT 0" },
|
|
67005
|
+
{ name: "edge_count", sql: "BIGINT NOT NULL DEFAULT 0" },
|
|
67006
|
+
// Generator metadata (for drift diagnostics — what hivemind version produced this?)
|
|
67007
|
+
{ name: "generator", sql: "TEXT NOT NULL DEFAULT 'hivemind-graph'" },
|
|
67008
|
+
{ name: "generator_version", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
67009
|
+
{ name: "schema_version", sql: "BIGINT NOT NULL DEFAULT 1" }
|
|
67010
|
+
]);
|
|
66912
67011
|
validateSchema("MEMORY_COLUMNS", MEMORY_COLUMNS);
|
|
66913
67012
|
validateSchema("SESSIONS_COLUMNS", SESSIONS_COLUMNS);
|
|
66914
67013
|
validateSchema("SKILLS_COLUMNS", SKILLS_COLUMNS);
|
|
67014
|
+
validateSchema("RULES_COLUMNS", RULES_COLUMNS);
|
|
67015
|
+
validateSchema("TASKS_COLUMNS", TASKS_COLUMNS);
|
|
67016
|
+
validateSchema("TASK_EVENTS_COLUMNS", TASK_EVENTS_COLUMNS);
|
|
67017
|
+
validateSchema("GOALS_COLUMNS", GOALS_COLUMNS);
|
|
67018
|
+
validateSchema("KPIS_COLUMNS", KPIS_COLUMNS);
|
|
67019
|
+
validateSchema("CODEBASE_COLUMNS", CODEBASE_COLUMNS);
|
|
66915
67020
|
function buildCreateTableSql(tableName, cols) {
|
|
66916
67021
|
const safe = sqlIdent(tableName);
|
|
66917
67022
|
const colSql = cols.map((c15) => `${c15.name} ${c15.sql}`).join(", ");
|
|
@@ -67136,7 +67241,7 @@ function getQueryTimeoutMs() {
|
|
|
67136
67241
|
return Number(process.env.HIVEMIND_QUERY_TIMEOUT_MS ?? 1e4);
|
|
67137
67242
|
}
|
|
67138
67243
|
function sleep2(ms3) {
|
|
67139
|
-
return new Promise((
|
|
67244
|
+
return new Promise((resolve7) => setTimeout(resolve7, ms3));
|
|
67140
67245
|
}
|
|
67141
67246
|
function isTimeoutError(error) {
|
|
67142
67247
|
const name = error instanceof Error ? error.name.toLowerCase() : "";
|
|
@@ -67166,7 +67271,7 @@ var Semaphore = class {
|
|
|
67166
67271
|
this.active++;
|
|
67167
67272
|
return;
|
|
67168
67273
|
}
|
|
67169
|
-
await new Promise((
|
|
67274
|
+
await new Promise((resolve7) => this.waiting.push(resolve7));
|
|
67170
67275
|
}
|
|
67171
67276
|
release() {
|
|
67172
67277
|
this.active--;
|
|
@@ -67476,6 +67581,24 @@ var DeeplakeApi = class {
|
|
|
67476
67581
|
* This sidesteps the Deeplake UPDATE-coalescing quirk that bit the wiki
|
|
67477
67582
|
* worker.
|
|
67478
67583
|
*/
|
|
67584
|
+
/**
|
|
67585
|
+
* Create the codebase table. One row per (org, workspace, repo, user,
|
|
67586
|
+
* worktree, commit) — see CODEBASE_COLUMNS for the schema. Healing
|
|
67587
|
+
* + index follow the same pattern as ensureSessionsTable.
|
|
67588
|
+
*/
|
|
67589
|
+
async ensureCodebaseTable(name) {
|
|
67590
|
+
const safe = sqlIdent(name);
|
|
67591
|
+
const tables = await this.listTables();
|
|
67592
|
+
if (!tables.includes(safe)) {
|
|
67593
|
+
log3(`table "${safe}" not found, creating`);
|
|
67594
|
+
await this.createTableWithRetry(buildCreateTableSql(safe, CODEBASE_COLUMNS), safe);
|
|
67595
|
+
log3(`table "${safe}" created`);
|
|
67596
|
+
if (!tables.includes(safe))
|
|
67597
|
+
this._tablesCache = [...tables, safe];
|
|
67598
|
+
}
|
|
67599
|
+
await this.healSchema(safe, CODEBASE_COLUMNS);
|
|
67600
|
+
await this.ensureLookupIndex(safe, "codebase_identity", `("org_id", "workspace_id", "repo_slug", "user_id", "worktree_id", "commit_sha")`);
|
|
67601
|
+
}
|
|
67479
67602
|
async ensureSkillsTable(name) {
|
|
67480
67603
|
const safe = sqlIdent(name);
|
|
67481
67604
|
const tables = await this.listTables();
|
|
@@ -67489,13 +67612,118 @@ var DeeplakeApi = class {
|
|
|
67489
67612
|
await this.healSchema(safe, SKILLS_COLUMNS);
|
|
67490
67613
|
await this.ensureLookupIndex(safe, "project_key_name", `("project_key", "name")`);
|
|
67491
67614
|
}
|
|
67615
|
+
/**
|
|
67616
|
+
* Create the rules table.
|
|
67617
|
+
*
|
|
67618
|
+
* One row per rule version (same write pattern as skills): edits INSERT
|
|
67619
|
+
* a fresh row with version+1, reads pick latest per rule_id via
|
|
67620
|
+
* `ORDER BY version DESC LIMIT 1`. Sidesteps the Deeplake
|
|
67621
|
+
* UPDATE-coalescing quirk by never UPDATEing.
|
|
67622
|
+
*/
|
|
67623
|
+
async ensureRulesTable(name) {
|
|
67624
|
+
const safe = sqlIdent(name);
|
|
67625
|
+
const tables = await this.listTables();
|
|
67626
|
+
if (!tables.includes(safe)) {
|
|
67627
|
+
log3(`table "${safe}" not found, creating`);
|
|
67628
|
+
await this.createTableWithRetry(buildCreateTableSql(safe, RULES_COLUMNS), safe);
|
|
67629
|
+
log3(`table "${safe}" created`);
|
|
67630
|
+
if (!tables.includes(safe))
|
|
67631
|
+
this._tablesCache = [...tables, safe];
|
|
67632
|
+
}
|
|
67633
|
+
await this.healSchema(safe, RULES_COLUMNS);
|
|
67634
|
+
await this.ensureLookupIndex(safe, "rule_id_version", `("rule_id", "version")`);
|
|
67635
|
+
}
|
|
67636
|
+
/**
|
|
67637
|
+
* Create the tasks table.
|
|
67638
|
+
*
|
|
67639
|
+
* Same write pattern as rules + skills. `kpis` is a nullable JSONB
|
|
67640
|
+
* column with the agent's KPI metadata; KPI current values come from
|
|
67641
|
+
* `task_events` (SUM(value)), not this snapshot.
|
|
67642
|
+
*/
|
|
67643
|
+
async ensureTasksTable(name) {
|
|
67644
|
+
const safe = sqlIdent(name);
|
|
67645
|
+
const tables = await this.listTables();
|
|
67646
|
+
if (!tables.includes(safe)) {
|
|
67647
|
+
log3(`table "${safe}" not found, creating`);
|
|
67648
|
+
await this.createTableWithRetry(buildCreateTableSql(safe, TASKS_COLUMNS), safe);
|
|
67649
|
+
log3(`table "${safe}" created`);
|
|
67650
|
+
if (!tables.includes(safe))
|
|
67651
|
+
this._tablesCache = [...tables, safe];
|
|
67652
|
+
}
|
|
67653
|
+
await this.healSchema(safe, TASKS_COLUMNS);
|
|
67654
|
+
await this.ensureLookupIndex(safe, "task_id_version", `("task_id", "version")`);
|
|
67655
|
+
}
|
|
67656
|
+
/**
|
|
67657
|
+
* Create the task-events table.
|
|
67658
|
+
*
|
|
67659
|
+
* Append-only. Every INSERT is a fresh row; never UPDATE. KPI current
|
|
67660
|
+
* value is `SUM(value) WHERE task_id=? AND kpi_id=?`. Index on
|
|
67661
|
+
* (task_id, kpi_id) is the canonical aggregation key.
|
|
67662
|
+
*/
|
|
67663
|
+
async ensureTaskEventsTable(name) {
|
|
67664
|
+
const safe = sqlIdent(name);
|
|
67665
|
+
const tables = await this.listTables();
|
|
67666
|
+
if (!tables.includes(safe)) {
|
|
67667
|
+
log3(`table "${safe}" not found, creating`);
|
|
67668
|
+
await this.createTableWithRetry(buildCreateTableSql(safe, TASK_EVENTS_COLUMNS), safe);
|
|
67669
|
+
log3(`table "${safe}" created`);
|
|
67670
|
+
if (!tables.includes(safe))
|
|
67671
|
+
this._tablesCache = [...tables, safe];
|
|
67672
|
+
}
|
|
67673
|
+
await this.healSchema(safe, TASK_EVENTS_COLUMNS);
|
|
67674
|
+
await this.ensureLookupIndex(safe, "task_id_kpi_id", `("task_id", "kpi_id")`);
|
|
67675
|
+
}
|
|
67676
|
+
/**
|
|
67677
|
+
* Create the goals table.
|
|
67678
|
+
*
|
|
67679
|
+
* Backed by the VFS path convention memory/goal/<owner>/<status>/<goal_id>.md.
|
|
67680
|
+
* INSERT-only version-bumped: rm and mv operations translate to fresh
|
|
67681
|
+
* v=N+1 rows (status flips for mv → closed; rm is the same soft-close).
|
|
67682
|
+
* The (goal_id, version) index lets the VFS dispatch a cheap latest-row
|
|
67683
|
+
* read on cat / Read of a single goal.
|
|
67684
|
+
*/
|
|
67685
|
+
async ensureGoalsTable(name) {
|
|
67686
|
+
const safe = sqlIdent(name);
|
|
67687
|
+
const tables = await this.listTables();
|
|
67688
|
+
if (!tables.includes(safe)) {
|
|
67689
|
+
log3(`table "${safe}" not found, creating`);
|
|
67690
|
+
await this.createTableWithRetry(buildCreateTableSql(safe, GOALS_COLUMNS), safe);
|
|
67691
|
+
log3(`table "${safe}" created`);
|
|
67692
|
+
if (!tables.includes(safe))
|
|
67693
|
+
this._tablesCache = [...tables, safe];
|
|
67694
|
+
}
|
|
67695
|
+
await this.healSchema(safe, GOALS_COLUMNS);
|
|
67696
|
+
await this.ensureLookupIndex(safe, "goal_id_version", `("goal_id", "version")`);
|
|
67697
|
+
await this.ensureLookupIndex(safe, "owner_status", `("owner", "status")`);
|
|
67698
|
+
}
|
|
67699
|
+
/**
|
|
67700
|
+
* Create the kpis table.
|
|
67701
|
+
*
|
|
67702
|
+
* Backed by memory/kpi/<goal_id>/<kpi_id>.md. KPI rows do NOT carry
|
|
67703
|
+
* owner — ownership derives from the parent goal via logical join on
|
|
67704
|
+
* goal_id. INSERT-only version-bumped. (goal_id, kpi_id) index is the
|
|
67705
|
+
* canonical lookup the VFS uses on Read and Write.
|
|
67706
|
+
*/
|
|
67707
|
+
async ensureKpisTable(name) {
|
|
67708
|
+
const safe = sqlIdent(name);
|
|
67709
|
+
const tables = await this.listTables();
|
|
67710
|
+
if (!tables.includes(safe)) {
|
|
67711
|
+
log3(`table "${safe}" not found, creating`);
|
|
67712
|
+
await this.createTableWithRetry(buildCreateTableSql(safe, KPIS_COLUMNS), safe);
|
|
67713
|
+
log3(`table "${safe}" created`);
|
|
67714
|
+
if (!tables.includes(safe))
|
|
67715
|
+
this._tablesCache = [...tables, safe];
|
|
67716
|
+
}
|
|
67717
|
+
await this.healSchema(safe, KPIS_COLUMNS);
|
|
67718
|
+
await this.ensureLookupIndex(safe, "goal_id_kpi_id", `("goal_id", "kpi_id")`);
|
|
67719
|
+
}
|
|
67492
67720
|
};
|
|
67493
67721
|
|
|
67494
67722
|
// dist/src/shell/deeplake-fs.js
|
|
67495
|
-
import { basename as
|
|
67723
|
+
import { basename as basename5, posix } from "node:path";
|
|
67496
67724
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
67497
67725
|
import { fileURLToPath } from "node:url";
|
|
67498
|
-
import { dirname as
|
|
67726
|
+
import { dirname as dirname9, join as join16 } from "node:path";
|
|
67499
67727
|
|
|
67500
67728
|
// dist/src/shell/grep-core.js
|
|
67501
67729
|
var TOOL_INPUT_FIELDS = [
|
|
@@ -68176,7 +68404,7 @@ var EmbedClient = class {
|
|
|
68176
68404
|
}
|
|
68177
68405
|
}
|
|
68178
68406
|
connectOnce() {
|
|
68179
|
-
return new Promise((
|
|
68407
|
+
return new Promise((resolve7, reject) => {
|
|
68180
68408
|
const sock = connect(this.socketPath);
|
|
68181
68409
|
const to3 = setTimeout(() => {
|
|
68182
68410
|
sock.destroy();
|
|
@@ -68184,7 +68412,7 @@ var EmbedClient = class {
|
|
|
68184
68412
|
}, this.timeoutMs);
|
|
68185
68413
|
sock.once("connect", () => {
|
|
68186
68414
|
clearTimeout(to3);
|
|
68187
|
-
|
|
68415
|
+
resolve7(sock);
|
|
68188
68416
|
});
|
|
68189
68417
|
sock.once("error", (e6) => {
|
|
68190
68418
|
clearTimeout(to3);
|
|
@@ -68266,7 +68494,7 @@ var EmbedClient = class {
|
|
|
68266
68494
|
throw new Error("daemon did not become ready within spawnWaitMs");
|
|
68267
68495
|
}
|
|
68268
68496
|
sendAndWait(sock, req) {
|
|
68269
|
-
return new Promise((
|
|
68497
|
+
return new Promise((resolve7, reject) => {
|
|
68270
68498
|
let buf = "";
|
|
68271
68499
|
const to3 = setTimeout(() => {
|
|
68272
68500
|
sock.destroy();
|
|
@@ -68281,7 +68509,7 @@ var EmbedClient = class {
|
|
|
68281
68509
|
const line = buf.slice(0, nl3);
|
|
68282
68510
|
clearTimeout(to3);
|
|
68283
68511
|
try {
|
|
68284
|
-
|
|
68512
|
+
resolve7(JSON.parse(line));
|
|
68285
68513
|
} catch (e6) {
|
|
68286
68514
|
reject(e6);
|
|
68287
68515
|
}
|
|
@@ -68501,6 +68729,516 @@ function buildVirtualIndexContent(summaryRows, sessionRows = [], opts = {}) {
|
|
|
68501
68729
|
return lines.join("\n");
|
|
68502
68730
|
}
|
|
68503
68731
|
|
|
68732
|
+
// dist/src/shell/goal-paths.js
|
|
68733
|
+
var VALID_STATUS = /* @__PURE__ */ new Set(["opened", "in_progress", "closed"]);
|
|
68734
|
+
function segmentsUnderMemory(p22) {
|
|
68735
|
+
let s10 = p22.replace(/\/+$/, "");
|
|
68736
|
+
const memIdx = s10.lastIndexOf("/memory/");
|
|
68737
|
+
if (memIdx >= 0) {
|
|
68738
|
+
s10 = s10.slice(memIdx + "/memory/".length);
|
|
68739
|
+
} else {
|
|
68740
|
+
s10 = s10.replace(/^\/+/, "");
|
|
68741
|
+
if (s10 === "memory")
|
|
68742
|
+
return null;
|
|
68743
|
+
if (s10.startsWith("memory/"))
|
|
68744
|
+
s10 = s10.slice("memory/".length);
|
|
68745
|
+
}
|
|
68746
|
+
if (s10.length === 0)
|
|
68747
|
+
return null;
|
|
68748
|
+
return s10.split("/");
|
|
68749
|
+
}
|
|
68750
|
+
function classifyPath(p22) {
|
|
68751
|
+
const segs = segmentsUnderMemory(p22);
|
|
68752
|
+
if (!segs)
|
|
68753
|
+
return "memory";
|
|
68754
|
+
if (segs[0] === "goal") {
|
|
68755
|
+
if (segs.length === 4 && segs[3].endsWith(".md") && VALID_STATUS.has(segs[2])) {
|
|
68756
|
+
return "goal";
|
|
68757
|
+
}
|
|
68758
|
+
return "memory";
|
|
68759
|
+
}
|
|
68760
|
+
if (segs[0] === "kpi") {
|
|
68761
|
+
if (segs.length === 3 && segs[2].endsWith(".md")) {
|
|
68762
|
+
return "kpi";
|
|
68763
|
+
}
|
|
68764
|
+
return "memory";
|
|
68765
|
+
}
|
|
68766
|
+
return "memory";
|
|
68767
|
+
}
|
|
68768
|
+
function decomposeGoalPath(p22) {
|
|
68769
|
+
const segs = segmentsUnderMemory(p22);
|
|
68770
|
+
if (!segs || segs.length !== 4 || segs[0] !== "goal") {
|
|
68771
|
+
throw new Error(`Not a goal path: ${p22}`);
|
|
68772
|
+
}
|
|
68773
|
+
const status = segs[2];
|
|
68774
|
+
if (!VALID_STATUS.has(status)) {
|
|
68775
|
+
throw new Error(`Invalid goal status in path: ${p22} (got '${status}')`);
|
|
68776
|
+
}
|
|
68777
|
+
const filename = segs[3];
|
|
68778
|
+
if (!filename.endsWith(".md")) {
|
|
68779
|
+
throw new Error(`Goal path must end with .md: ${p22}`);
|
|
68780
|
+
}
|
|
68781
|
+
return {
|
|
68782
|
+
owner: segs[1],
|
|
68783
|
+
status,
|
|
68784
|
+
goal_id: filename.slice(0, -".md".length)
|
|
68785
|
+
};
|
|
68786
|
+
}
|
|
68787
|
+
function decomposeKpiPath(p22) {
|
|
68788
|
+
const segs = segmentsUnderMemory(p22);
|
|
68789
|
+
if (!segs || segs.length !== 3 || segs[0] !== "kpi") {
|
|
68790
|
+
throw new Error(`Not a kpi path: ${p22}`);
|
|
68791
|
+
}
|
|
68792
|
+
const filename = segs[2];
|
|
68793
|
+
if (!filename.endsWith(".md")) {
|
|
68794
|
+
throw new Error(`KPI path must end with .md: ${p22}`);
|
|
68795
|
+
}
|
|
68796
|
+
return {
|
|
68797
|
+
goal_id: segs[1],
|
|
68798
|
+
kpi_id: filename.slice(0, -".md".length)
|
|
68799
|
+
};
|
|
68800
|
+
}
|
|
68801
|
+
function composeGoalPath(parts) {
|
|
68802
|
+
return `/goal/${parts.owner}/${parts.status}/${parts.goal_id}.md`;
|
|
68803
|
+
}
|
|
68804
|
+
function composeKpiPath(parts) {
|
|
68805
|
+
return `/kpi/${parts.goal_id}/${parts.kpi_id}.md`;
|
|
68806
|
+
}
|
|
68807
|
+
|
|
68808
|
+
// dist/src/graph/vfs-handler.js
|
|
68809
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync8, readFileSync as readFileSync9, renameSync as renameSync5, writeFileSync as writeFileSync7 } from "node:fs";
|
|
68810
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
68811
|
+
import { join as join15, dirname as dirname8 } from "node:path";
|
|
68812
|
+
|
|
68813
|
+
// dist/src/graph/last-build.js
|
|
68814
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync5, readFileSync as readFileSync7, renameSync as renameSync3, writeFileSync as writeFileSync5 } from "node:fs";
|
|
68815
|
+
import { dirname as dirname5, join as join12 } from "node:path";
|
|
68816
|
+
function lastBuildPath(baseDir, worktreeId) {
|
|
68817
|
+
if (worktreeId !== void 0) {
|
|
68818
|
+
return join12(baseDir, "worktrees", worktreeId, ".last-build.json");
|
|
68819
|
+
}
|
|
68820
|
+
return join12(baseDir, ".last-build.json");
|
|
68821
|
+
}
|
|
68822
|
+
function readLastBuild(baseDir, worktreeId) {
|
|
68823
|
+
let path2 = lastBuildPath(baseDir, worktreeId);
|
|
68824
|
+
if (!existsSync6(path2)) {
|
|
68825
|
+
if (worktreeId === void 0)
|
|
68826
|
+
return null;
|
|
68827
|
+
const legacy = lastBuildPath(baseDir, void 0);
|
|
68828
|
+
if (!existsSync6(legacy))
|
|
68829
|
+
return null;
|
|
68830
|
+
path2 = legacy;
|
|
68831
|
+
}
|
|
68832
|
+
let raw;
|
|
68833
|
+
try {
|
|
68834
|
+
raw = readFileSync7(path2, "utf8");
|
|
68835
|
+
} catch {
|
|
68836
|
+
return null;
|
|
68837
|
+
}
|
|
68838
|
+
let parsed;
|
|
68839
|
+
try {
|
|
68840
|
+
parsed = JSON.parse(raw);
|
|
68841
|
+
} catch {
|
|
68842
|
+
return null;
|
|
68843
|
+
}
|
|
68844
|
+
if (parsed === null || typeof parsed !== "object")
|
|
68845
|
+
return null;
|
|
68846
|
+
const o14 = parsed;
|
|
68847
|
+
if (typeof o14.ts !== "number" || !Number.isFinite(o14.ts))
|
|
68848
|
+
return null;
|
|
68849
|
+
if (o14.commit_sha !== null && typeof o14.commit_sha !== "string")
|
|
68850
|
+
return null;
|
|
68851
|
+
if (typeof o14.snapshot_sha256 !== "string")
|
|
68852
|
+
return null;
|
|
68853
|
+
const out = { ts: o14.ts, commit_sha: o14.commit_sha, snapshot_sha256: o14.snapshot_sha256 };
|
|
68854
|
+
if (typeof o14.node_count === "number" && Number.isFinite(o14.node_count) && o14.node_count >= 0) {
|
|
68855
|
+
out.node_count = o14.node_count;
|
|
68856
|
+
}
|
|
68857
|
+
if (typeof o14.edge_count === "number" && Number.isFinite(o14.edge_count) && o14.edge_count >= 0) {
|
|
68858
|
+
out.edge_count = o14.edge_count;
|
|
68859
|
+
}
|
|
68860
|
+
return out;
|
|
68861
|
+
}
|
|
68862
|
+
|
|
68863
|
+
// dist/src/graph/snapshot.js
|
|
68864
|
+
import { createHash } from "node:crypto";
|
|
68865
|
+
import { mkdirSync as mkdirSync7, renameSync as renameSync4, writeFileSync as writeFileSync6 } from "node:fs";
|
|
68866
|
+
import { homedir as homedir8 } from "node:os";
|
|
68867
|
+
import { dirname as dirname7, join as join14 } from "node:path";
|
|
68868
|
+
|
|
68869
|
+
// dist/src/graph/history.js
|
|
68870
|
+
import { appendFileSync as appendFileSync2, existsSync as existsSync7, mkdirSync as mkdirSync6, readFileSync as readFileSync8 } from "node:fs";
|
|
68871
|
+
import { dirname as dirname6, join as join13 } from "node:path";
|
|
68872
|
+
|
|
68873
|
+
// dist/src/graph/snapshot.js
|
|
68874
|
+
function graphsRoot() {
|
|
68875
|
+
return process.env.HIVEMIND_GRAPHS_HOME ?? join14(homedir8(), ".hivemind", "graphs");
|
|
68876
|
+
}
|
|
68877
|
+
function repoDir(repoKey) {
|
|
68878
|
+
return join14(graphsRoot(), repoKey);
|
|
68879
|
+
}
|
|
68880
|
+
|
|
68881
|
+
// dist/src/utils/repo-identity.js
|
|
68882
|
+
import { execSync } from "node:child_process";
|
|
68883
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
68884
|
+
import { basename as basename4, resolve as resolve5 } from "node:path";
|
|
68885
|
+
var DEFAULT_PORTS = {
|
|
68886
|
+
http: "80",
|
|
68887
|
+
https: "443",
|
|
68888
|
+
ssh: "22",
|
|
68889
|
+
git: "9418"
|
|
68890
|
+
};
|
|
68891
|
+
function normalizeGitRemoteUrl(url) {
|
|
68892
|
+
let s10 = url.trim();
|
|
68893
|
+
const schemeMatch = s10.match(/^([a-z][a-z0-9+.-]*):\/\//i);
|
|
68894
|
+
const scheme = schemeMatch ? schemeMatch[1].toLowerCase() : null;
|
|
68895
|
+
if (schemeMatch)
|
|
68896
|
+
s10 = s10.slice(schemeMatch[0].length);
|
|
68897
|
+
if (!scheme) {
|
|
68898
|
+
const scp = s10.match(/^(?:[^@/\s]+@)?([^:/\s]+):(.+)$/);
|
|
68899
|
+
if (scp)
|
|
68900
|
+
s10 = `${scp[1]}/${scp[2]}`;
|
|
68901
|
+
}
|
|
68902
|
+
s10 = s10.replace(/^[^@/]+@/, "");
|
|
68903
|
+
if (scheme && DEFAULT_PORTS[scheme]) {
|
|
68904
|
+
s10 = s10.replace(new RegExp(`^([^/]+):${DEFAULT_PORTS[scheme]}(/|$)`), "$1$2");
|
|
68905
|
+
}
|
|
68906
|
+
s10 = s10.replace(/\.git\/?$/i, "");
|
|
68907
|
+
s10 = s10.replace(/\/+$/, "");
|
|
68908
|
+
return s10.toLowerCase();
|
|
68909
|
+
}
|
|
68910
|
+
function deriveProjectKey(cwd) {
|
|
68911
|
+
const absCwd = resolve5(cwd);
|
|
68912
|
+
const project = basename4(absCwd) || "unknown";
|
|
68913
|
+
let signature = null;
|
|
68914
|
+
try {
|
|
68915
|
+
const raw = execSync("git config --get remote.origin.url", {
|
|
68916
|
+
cwd: absCwd,
|
|
68917
|
+
encoding: "utf-8",
|
|
68918
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
68919
|
+
}).trim();
|
|
68920
|
+
signature = raw ? normalizeGitRemoteUrl(raw) : null;
|
|
68921
|
+
} catch {
|
|
68922
|
+
}
|
|
68923
|
+
const input = signature ?? absCwd;
|
|
68924
|
+
const key = createHash2("sha1").update(input).digest("hex").slice(0, 16);
|
|
68925
|
+
return { key, project };
|
|
68926
|
+
}
|
|
68927
|
+
|
|
68928
|
+
// dist/src/graph/vfs-handler.js
|
|
68929
|
+
function workTreeIdFor(cwd) {
|
|
68930
|
+
return createHash3("sha256").update(cwd).digest("hex").slice(0, 16);
|
|
68931
|
+
}
|
|
68932
|
+
function handleGraphVfs(subpath, cwd) {
|
|
68933
|
+
const path2 = subpath.replace(/^\/+/, "");
|
|
68934
|
+
if (path2 === "" || path2 === "/") {
|
|
68935
|
+
return { kind: "ok", body: dirListing() };
|
|
68936
|
+
}
|
|
68937
|
+
if (path2 === "index.md" || path2 === "index") {
|
|
68938
|
+
return loadSnapshotOrError(cwd, (snap, baseDir) => ({
|
|
68939
|
+
kind: "ok",
|
|
68940
|
+
body: renderIndex(snap, baseDir, cwd)
|
|
68941
|
+
}));
|
|
68942
|
+
}
|
|
68943
|
+
if (path2.startsWith("find/")) {
|
|
68944
|
+
const pattern = path2.slice("find/".length);
|
|
68945
|
+
if (pattern === "") {
|
|
68946
|
+
return { kind: "not-found", message: "find/ requires a pattern: cat memory/graph/find/<keyword>" };
|
|
68947
|
+
}
|
|
68948
|
+
return loadSnapshotOrError(cwd, (snap, baseDir) => ({
|
|
68949
|
+
kind: "ok",
|
|
68950
|
+
body: renderFind(snap, pattern, baseDir, workTreeIdFor(cwd))
|
|
68951
|
+
}));
|
|
68952
|
+
}
|
|
68953
|
+
if (path2.startsWith("show/")) {
|
|
68954
|
+
const key = path2.slice("show/".length);
|
|
68955
|
+
if (key === "") {
|
|
68956
|
+
return { kind: "not-found", message: "show/ requires a handle or pattern" };
|
|
68957
|
+
}
|
|
68958
|
+
return loadSnapshotOrError(cwd, (snap, baseDir) => ({
|
|
68959
|
+
kind: "ok",
|
|
68960
|
+
body: renderShow(snap, key, baseDir, workTreeIdFor(cwd))
|
|
68961
|
+
}));
|
|
68962
|
+
}
|
|
68963
|
+
return {
|
|
68964
|
+
kind: "not-found",
|
|
68965
|
+
message: `Unknown endpoint: graph/${path2}
|
|
68966
|
+
Available: index.md, find/<pattern>, show/<handle-or-pattern>`
|
|
68967
|
+
};
|
|
68968
|
+
}
|
|
68969
|
+
function loadSnapshotOrError(cwd, fn4) {
|
|
68970
|
+
let key;
|
|
68971
|
+
let baseDir;
|
|
68972
|
+
try {
|
|
68973
|
+
key = deriveProjectKey(cwd).key;
|
|
68974
|
+
baseDir = repoDir(key);
|
|
68975
|
+
} catch (e6) {
|
|
68976
|
+
return { kind: "no-graph", message: `Cannot derive repo identity: ${e6 instanceof Error ? e6.message : String(e6)}` };
|
|
68977
|
+
}
|
|
68978
|
+
const wt3 = workTreeIdFor(cwd);
|
|
68979
|
+
const last = readLastBuild(baseDir, wt3);
|
|
68980
|
+
if (last === null) {
|
|
68981
|
+
return {
|
|
68982
|
+
kind: "no-graph",
|
|
68983
|
+
message: "No local graph for this worktree yet. Run `hivemind graph build` (or `hivemind graph pull` if a teammate has built this commit)."
|
|
68984
|
+
};
|
|
68985
|
+
}
|
|
68986
|
+
const fileBase = last.commit_sha ?? last.snapshot_sha256;
|
|
68987
|
+
const snapPath = join15(baseDir, "snapshots", `${fileBase}.json`);
|
|
68988
|
+
if (!existsSync8(snapPath)) {
|
|
68989
|
+
return { kind: "no-graph", message: `Snapshot file missing on disk: ${snapPath}` };
|
|
68990
|
+
}
|
|
68991
|
+
let snap;
|
|
68992
|
+
try {
|
|
68993
|
+
snap = JSON.parse(readFileSync9(snapPath, "utf8"));
|
|
68994
|
+
} catch (e6) {
|
|
68995
|
+
return { kind: "no-graph", message: `Failed to parse snapshot: ${e6 instanceof Error ? e6.message : String(e6)}` };
|
|
68996
|
+
}
|
|
68997
|
+
if (!Array.isArray(snap.nodes) || !Array.isArray(snap.links)) {
|
|
68998
|
+
return { kind: "no-graph", message: "Snapshot schema is invalid (missing nodes/links arrays)." };
|
|
68999
|
+
}
|
|
69000
|
+
try {
|
|
69001
|
+
return fn4(snap, baseDir);
|
|
69002
|
+
} catch (e6) {
|
|
69003
|
+
return { kind: "no-graph", message: `Failed to render graph view: ${e6 instanceof Error ? e6.message : String(e6)}` };
|
|
69004
|
+
}
|
|
69005
|
+
}
|
|
69006
|
+
function dirListing() {
|
|
69007
|
+
return [
|
|
69008
|
+
"index.md",
|
|
69009
|
+
"find/",
|
|
69010
|
+
"show/"
|
|
69011
|
+
].join("\n");
|
|
69012
|
+
}
|
|
69013
|
+
function renderIndex(snap, baseDir, cwd) {
|
|
69014
|
+
const commit = snap.graph.commit_sha?.slice(0, 7) ?? "no-commit";
|
|
69015
|
+
const fullCommit = snap.graph.commit_sha ?? "no-commit";
|
|
69016
|
+
const totalNodes = snap.nodes.length;
|
|
69017
|
+
const totalEdges = snap.links.length;
|
|
69018
|
+
const byFile = {};
|
|
69019
|
+
for (const n24 of snap.nodes)
|
|
69020
|
+
byFile[n24.source_file] = (byFile[n24.source_file] ?? 0) + 1;
|
|
69021
|
+
const topFiles = Object.entries(byFile).sort(([, a15], [, b26]) => b26 - a15).slice(0, 8);
|
|
69022
|
+
const byRel = {};
|
|
69023
|
+
for (const e6 of snap.links)
|
|
69024
|
+
byRel[e6.relation] = (byRel[e6.relation] ?? 0) + 1;
|
|
69025
|
+
const byKind = {};
|
|
69026
|
+
for (const n24 of snap.nodes)
|
|
69027
|
+
byKind[n24.kind] = (byKind[n24.kind] ?? 0) + 1;
|
|
69028
|
+
const lines = [];
|
|
69029
|
+
lines.push(`# Code Graph \u2014 ${snap.observation.repo_project}`);
|
|
69030
|
+
lines.push("");
|
|
69031
|
+
lines.push(`Commit: ${fullCommit} (built ${snap.observation.ts})`);
|
|
69032
|
+
lines.push(`Branch: ${snap.observation.branch ?? "(detached)"}`);
|
|
69033
|
+
lines.push(`Source: ${join15(baseDir, "snapshots", `${commit ? snap.graph.commit_sha : "?"}.json`)}`);
|
|
69034
|
+
lines.push("");
|
|
69035
|
+
lines.push(`Nodes: ${totalNodes} Edges: ${totalEdges}`);
|
|
69036
|
+
lines.push("");
|
|
69037
|
+
lines.push("## How to query");
|
|
69038
|
+
lines.push(" cat ~/.deeplake/memory/graph/find/<pattern>");
|
|
69039
|
+
lines.push(" Case-insensitive substring match on node id + label.");
|
|
69040
|
+
lines.push(" Emits numbered handles [1] [2] ... saved for this worktree.");
|
|
69041
|
+
lines.push("");
|
|
69042
|
+
lines.push(" cat ~/.deeplake/memory/graph/show/<handle-or-pattern>");
|
|
69043
|
+
lines.push(" <handle>: a digit from a prior `find/` (e.g. 3).");
|
|
69044
|
+
lines.push(" <pattern>: a substring; resolves to a unique node if possible,");
|
|
69045
|
+
lines.push(" or shows candidates if ambiguous.");
|
|
69046
|
+
lines.push(" Output: node detail + 1-hop neighbors grouped by edge kind.");
|
|
69047
|
+
lines.push("");
|
|
69048
|
+
lines.push("## Node kinds");
|
|
69049
|
+
for (const [k17, n24] of Object.entries(byKind).sort(([, a15], [, b26]) => b26 - a15)) {
|
|
69050
|
+
lines.push(` ${k17.padEnd(12)} ${n24}`);
|
|
69051
|
+
}
|
|
69052
|
+
lines.push("");
|
|
69053
|
+
lines.push("## Edge kinds");
|
|
69054
|
+
for (const [k17, n24] of Object.entries(byRel).sort(([, a15], [, b26]) => b26 - a15)) {
|
|
69055
|
+
lines.push(` ${k17.padEnd(12)} ${n24}`);
|
|
69056
|
+
}
|
|
69057
|
+
lines.push("");
|
|
69058
|
+
lines.push("## Top files by node count");
|
|
69059
|
+
for (const [f11, n24] of topFiles) {
|
|
69060
|
+
lines.push(` ${String(n24).padStart(4)} ${f11}`);
|
|
69061
|
+
}
|
|
69062
|
+
lines.push("");
|
|
69063
|
+
lines.push(`Limitations:`);
|
|
69064
|
+
lines.push(` - TypeScript only. AST-based, no semantic similarity edges.`);
|
|
69065
|
+
lines.push(` - 'calls' edges are intra-file only (Phase 1 extractor doesn't resolve cross-file).`);
|
|
69066
|
+
lines.push(` A node showing "Incoming (0)" may still have callers in OTHER files \u2014`);
|
|
69067
|
+
lines.push(` treat it as "no callers in the same file", not "unused".`);
|
|
69068
|
+
lines.push(` - 'imports' edges are file-level and DO span files; trust them.`);
|
|
69069
|
+
lines.push(` - Stale after edits \u2014 if a file's mtime is newer than the build, read the live source.`);
|
|
69070
|
+
void cwd;
|
|
69071
|
+
return lines.join("\n");
|
|
69072
|
+
}
|
|
69073
|
+
function renderFind(snap, pattern, baseDir, worktreeId) {
|
|
69074
|
+
const needle = pattern.toLowerCase();
|
|
69075
|
+
const matches = [];
|
|
69076
|
+
for (const n24 of snap.nodes) {
|
|
69077
|
+
const id = n24.id.toLowerCase();
|
|
69078
|
+
const lbl = n24.label.toLowerCase();
|
|
69079
|
+
if (id.includes(needle) || lbl.includes(needle))
|
|
69080
|
+
matches.push(n24);
|
|
69081
|
+
}
|
|
69082
|
+
matches.sort((a15, b26) => {
|
|
69083
|
+
const ra2 = rank(a15, needle);
|
|
69084
|
+
const rb = rank(b26, needle);
|
|
69085
|
+
if (ra2 !== rb)
|
|
69086
|
+
return ra2 - rb;
|
|
69087
|
+
return a15.id.localeCompare(b26.id);
|
|
69088
|
+
});
|
|
69089
|
+
const capped = matches.slice(0, 50);
|
|
69090
|
+
if (capped.length === 0) {
|
|
69091
|
+
return `No matches for "${pattern}" in ${snap.nodes.length} nodes.
|
|
69092
|
+
Try a shorter or different substring.`;
|
|
69093
|
+
}
|
|
69094
|
+
saveHandles(baseDir, worktreeId, capped.map((n24) => n24.id), pattern);
|
|
69095
|
+
const lines = [];
|
|
69096
|
+
lines.push(`${matches.length} match${matches.length === 1 ? "" : "es"} for "${pattern}"${matches.length > capped.length ? ` (showing first ${capped.length})` : ""}:`);
|
|
69097
|
+
lines.push("");
|
|
69098
|
+
for (let i11 = 0; i11 < capped.length; i11++) {
|
|
69099
|
+
const n24 = capped[i11];
|
|
69100
|
+
const tag = n24.exported ? "exported" : "internal";
|
|
69101
|
+
lines.push(` [${i11 + 1}] ${n24.id} ${n24.kind} (${tag})`);
|
|
69102
|
+
}
|
|
69103
|
+
lines.push("");
|
|
69104
|
+
lines.push("Use: cat ~/.deeplake/memory/graph/show/<N> to see node + 1-hop neighbors");
|
|
69105
|
+
return lines.join("\n");
|
|
69106
|
+
}
|
|
69107
|
+
function renderShow(snap, key, baseDir, worktreeId) {
|
|
69108
|
+
if (/^\d+$/.test(key)) {
|
|
69109
|
+
const idx = parseInt(key, 10);
|
|
69110
|
+
const handles = loadHandles(baseDir, worktreeId);
|
|
69111
|
+
if (handles === null) {
|
|
69112
|
+
return `Handle [${idx}] not resolvable: no recent find/ in this worktree. Run cat memory/graph/find/<pattern> first.`;
|
|
69113
|
+
}
|
|
69114
|
+
if (idx < 1 || idx > handles.ids.length) {
|
|
69115
|
+
return `Handle [${idx}] out of range. Last find/${handles.pattern} produced ${handles.ids.length} matches.`;
|
|
69116
|
+
}
|
|
69117
|
+
const nodeId = handles.ids[idx - 1];
|
|
69118
|
+
const node = snap.nodes.find((n24) => n24.id === nodeId);
|
|
69119
|
+
if (!node) {
|
|
69120
|
+
return `Handle [${idx}] points at "${nodeId}" but that node is no longer in the snapshot (graph rebuilt since last find?). Re-run find.`;
|
|
69121
|
+
}
|
|
69122
|
+
return renderNodeDetail(snap, node);
|
|
69123
|
+
}
|
|
69124
|
+
const needle = key.toLowerCase();
|
|
69125
|
+
const matches = snap.nodes.filter((n24) => n24.id.toLowerCase().includes(needle));
|
|
69126
|
+
if (matches.length === 0) {
|
|
69127
|
+
return `No node matches "${key}". Try cat memory/graph/find/${key} for fuzzy search.`;
|
|
69128
|
+
}
|
|
69129
|
+
if (matches.length === 1) {
|
|
69130
|
+
return renderNodeDetail(snap, matches[0]);
|
|
69131
|
+
}
|
|
69132
|
+
saveHandles(baseDir, worktreeId, matches.slice(0, 50).map((n24) => n24.id), key);
|
|
69133
|
+
const lines = [];
|
|
69134
|
+
lines.push(`"${key}" matches ${matches.length} nodes. Pick one:`);
|
|
69135
|
+
lines.push("");
|
|
69136
|
+
for (let i11 = 0; i11 < Math.min(matches.length, 50); i11++) {
|
|
69137
|
+
lines.push(` [${i11 + 1}] ${matches[i11].id}`);
|
|
69138
|
+
}
|
|
69139
|
+
lines.push("");
|
|
69140
|
+
lines.push("Use: cat ~/.deeplake/memory/graph/show/<N>");
|
|
69141
|
+
return lines.join("\n");
|
|
69142
|
+
}
|
|
69143
|
+
function renderNodeDetail(snap, node) {
|
|
69144
|
+
const incoming = [];
|
|
69145
|
+
const outgoing = [];
|
|
69146
|
+
for (const e6 of snap.links) {
|
|
69147
|
+
if (e6.target === node.id)
|
|
69148
|
+
incoming.push(e6);
|
|
69149
|
+
if (e6.source === node.id)
|
|
69150
|
+
outgoing.push(e6);
|
|
69151
|
+
}
|
|
69152
|
+
const groupBy = (es3) => {
|
|
69153
|
+
const m26 = /* @__PURE__ */ new Map();
|
|
69154
|
+
for (const e6 of es3) {
|
|
69155
|
+
const list = m26.get(e6.relation) ?? [];
|
|
69156
|
+
list.push(e6);
|
|
69157
|
+
m26.set(e6.relation, list);
|
|
69158
|
+
}
|
|
69159
|
+
return m26;
|
|
69160
|
+
};
|
|
69161
|
+
const inGrp = groupBy(incoming);
|
|
69162
|
+
const outGrp = groupBy(outgoing);
|
|
69163
|
+
const lines = [];
|
|
69164
|
+
lines.push(`Node: ${node.id}`);
|
|
69165
|
+
lines.push(` source: ${node.source_file}:${node.source_location}`);
|
|
69166
|
+
lines.push(` kind: ${node.kind}`);
|
|
69167
|
+
lines.push(` label: ${node.label}`);
|
|
69168
|
+
lines.push(` ${node.exported ? "exported" : "internal"}`);
|
|
69169
|
+
lines.push("");
|
|
69170
|
+
const inHint = incoming.length === 0 ? " \u2014 no edges from THIS file (intra-file only; may still be called from other files)" : ":";
|
|
69171
|
+
lines.push(`Incoming (${incoming.length})${inHint}`);
|
|
69172
|
+
for (const [rel, es3] of [...inGrp.entries()].sort(([a15], [b26]) => a15.localeCompare(b26))) {
|
|
69173
|
+
lines.push(` ${rel} (${es3.length}):`);
|
|
69174
|
+
for (const e6 of es3.slice(0, 20)) {
|
|
69175
|
+
lines.push(` ${e6.source}`);
|
|
69176
|
+
}
|
|
69177
|
+
if (es3.length > 20)
|
|
69178
|
+
lines.push(` ... and ${es3.length - 20} more`);
|
|
69179
|
+
}
|
|
69180
|
+
lines.push("");
|
|
69181
|
+
lines.push(`Outgoing (${outgoing.length})${outgoing.length === 0 ? " \u2014 this node has no edges out" : ":"}`);
|
|
69182
|
+
for (const [rel, es3] of [...outGrp.entries()].sort(([a15], [b26]) => a15.localeCompare(b26))) {
|
|
69183
|
+
lines.push(` ${rel} (${es3.length}):`);
|
|
69184
|
+
for (const e6 of es3.slice(0, 20)) {
|
|
69185
|
+
lines.push(` ${e6.target}`);
|
|
69186
|
+
}
|
|
69187
|
+
if (es3.length > 20)
|
|
69188
|
+
lines.push(` ... and ${es3.length - 20} more`);
|
|
69189
|
+
}
|
|
69190
|
+
return lines.join("\n");
|
|
69191
|
+
}
|
|
69192
|
+
function rank(n24, needle) {
|
|
69193
|
+
const lbl = n24.label.toLowerCase();
|
|
69194
|
+
const id = n24.id.toLowerCase();
|
|
69195
|
+
if (lbl === needle)
|
|
69196
|
+
return 0;
|
|
69197
|
+
if (lbl.startsWith(needle))
|
|
69198
|
+
return 1;
|
|
69199
|
+
if (lbl.includes(needle))
|
|
69200
|
+
return 2;
|
|
69201
|
+
if (id.includes(needle))
|
|
69202
|
+
return 3;
|
|
69203
|
+
return 4;
|
|
69204
|
+
}
|
|
69205
|
+
function handlesPath(baseDir, worktreeId) {
|
|
69206
|
+
return join15(baseDir, "worktrees", worktreeId, ".find-handles.json");
|
|
69207
|
+
}
|
|
69208
|
+
function saveHandles(baseDir, worktreeId, ids, pattern) {
|
|
69209
|
+
const path2 = handlesPath(baseDir, worktreeId);
|
|
69210
|
+
const payload = { pattern, ts: Date.now(), ids };
|
|
69211
|
+
try {
|
|
69212
|
+
mkdirSync8(dirname8(path2), { recursive: true });
|
|
69213
|
+
const tmp = `${path2}.tmp.${process.pid}.${Date.now()}`;
|
|
69214
|
+
writeFileSync7(tmp, JSON.stringify(payload));
|
|
69215
|
+
renameSync5(tmp, path2);
|
|
69216
|
+
} catch {
|
|
69217
|
+
}
|
|
69218
|
+
}
|
|
69219
|
+
function loadHandles(baseDir, worktreeId) {
|
|
69220
|
+
const path2 = handlesPath(baseDir, worktreeId);
|
|
69221
|
+
if (!existsSync8(path2))
|
|
69222
|
+
return null;
|
|
69223
|
+
try {
|
|
69224
|
+
const parsed = JSON.parse(readFileSync9(path2, "utf8"));
|
|
69225
|
+
if (parsed === null || typeof parsed !== "object")
|
|
69226
|
+
return null;
|
|
69227
|
+
const o14 = parsed;
|
|
69228
|
+
if (typeof o14.pattern !== "string")
|
|
69229
|
+
return null;
|
|
69230
|
+
if (typeof o14.ts !== "number")
|
|
69231
|
+
return null;
|
|
69232
|
+
if (!Array.isArray(o14.ids))
|
|
69233
|
+
return null;
|
|
69234
|
+
if (!o14.ids.every((s10) => typeof s10 === "string"))
|
|
69235
|
+
return null;
|
|
69236
|
+
return { pattern: o14.pattern, ts: o14.ts, ids: o14.ids };
|
|
69237
|
+
} catch {
|
|
69238
|
+
return null;
|
|
69239
|
+
}
|
|
69240
|
+
}
|
|
69241
|
+
|
|
68504
69242
|
// dist/src/shell/deeplake-fs.js
|
|
68505
69243
|
var BATCH_SIZE = 10;
|
|
68506
69244
|
var PREFETCH_BATCH_SIZE = 50;
|
|
@@ -68530,7 +69268,7 @@ function normalizeSessionMessage(path2, message) {
|
|
|
68530
69268
|
return normalizeContent(path2, raw);
|
|
68531
69269
|
}
|
|
68532
69270
|
function resolveEmbedDaemonPath() {
|
|
68533
|
-
return
|
|
69271
|
+
return join16(dirname9(fileURLToPath(import.meta.url)), "..", "embeddings", "embed-daemon.js");
|
|
68534
69272
|
}
|
|
68535
69273
|
function joinSessionMessages(path2, messages) {
|
|
68536
69274
|
return messages.map((message) => normalizeSessionMessage(path2, message)).join("\n");
|
|
@@ -68538,6 +69276,29 @@ function joinSessionMessages(path2, messages) {
|
|
|
68538
69276
|
function fsErr(code, msg, path2) {
|
|
68539
69277
|
return Object.assign(new Error(`${code}: ${msg}, '${path2}'`), { code });
|
|
68540
69278
|
}
|
|
69279
|
+
var GRAPH_ROOT = "/graph";
|
|
69280
|
+
var GRAPH_PREFIX = "/graph/";
|
|
69281
|
+
var GRAPH_DIRS = /* @__PURE__ */ new Set([GRAPH_ROOT, "/graph/find", "/graph/show"]);
|
|
69282
|
+
function isGraphPath(p22) {
|
|
69283
|
+
return p22 === GRAPH_ROOT || p22.startsWith(GRAPH_PREFIX);
|
|
69284
|
+
}
|
|
69285
|
+
function isGraphDir(p22) {
|
|
69286
|
+
return GRAPH_DIRS.has(p22);
|
|
69287
|
+
}
|
|
69288
|
+
function graphSubpathOf(p22) {
|
|
69289
|
+
if (p22 === GRAPH_ROOT)
|
|
69290
|
+
return "";
|
|
69291
|
+
return p22.slice(GRAPH_PREFIX.length);
|
|
69292
|
+
}
|
|
69293
|
+
function readGraphFile(p22, cwd) {
|
|
69294
|
+
const sub = graphSubpathOf(p22);
|
|
69295
|
+
const r10 = handleGraphVfs(sub, cwd);
|
|
69296
|
+
if (r10.kind === "ok")
|
|
69297
|
+
return r10.body;
|
|
69298
|
+
if (r10.kind === "no-graph")
|
|
69299
|
+
return `(no-graph) ${r10.message}`;
|
|
69300
|
+
throw fsErr("ENOENT", `${r10.message}`, p22);
|
|
69301
|
+
}
|
|
68541
69302
|
var DeeplakeFs = class _DeeplakeFs {
|
|
68542
69303
|
client;
|
|
68543
69304
|
table;
|
|
@@ -68561,6 +69322,12 @@ var DeeplakeFs = class _DeeplakeFs {
|
|
|
68561
69322
|
// Paths that live in the sessions table (multi-row, read by concatenation)
|
|
68562
69323
|
sessionPaths = /* @__PURE__ */ new Set();
|
|
68563
69324
|
sessionsTable = null;
|
|
69325
|
+
// Path-routed structured tables. When non-null, the VFS classifies
|
|
69326
|
+
// each path (see ./goal-paths.ts) and dispatches reads/writes to
|
|
69327
|
+
// the right table instead of the generic memory table. Null means
|
|
69328
|
+
// the goal/kpi routing is disabled (test or legacy configurations).
|
|
69329
|
+
goalsTable = null;
|
|
69330
|
+
kpisTable = null;
|
|
68564
69331
|
// Embedding client lazily created on first flush. Lives as long as the process.
|
|
68565
69332
|
embedClient = null;
|
|
68566
69333
|
constructor(client, table, mountPoint) {
|
|
@@ -68571,10 +69338,24 @@ var DeeplakeFs = class _DeeplakeFs {
|
|
|
68571
69338
|
if (mountPoint !== "/")
|
|
68572
69339
|
this.dirs.set("/", /* @__PURE__ */ new Set([mountPoint.slice(1)]));
|
|
68573
69340
|
}
|
|
68574
|
-
static async create(client, table, mount = "/memory", sessionsTable) {
|
|
69341
|
+
static async create(client, table, mount = "/memory", sessionsTable, extra) {
|
|
68575
69342
|
const fs3 = new _DeeplakeFs(client, table, mount);
|
|
68576
69343
|
fs3.sessionsTable = sessionsTable ?? null;
|
|
69344
|
+
fs3.goalsTable = extra?.goalsTable ?? null;
|
|
69345
|
+
fs3.kpisTable = extra?.kpisTable ?? null;
|
|
68577
69346
|
await client.ensureTable();
|
|
69347
|
+
if (fs3.goalsTable) {
|
|
69348
|
+
try {
|
|
69349
|
+
await client.ensureGoalsTable(fs3.goalsTable);
|
|
69350
|
+
} catch {
|
|
69351
|
+
}
|
|
69352
|
+
}
|
|
69353
|
+
if (fs3.kpisTable) {
|
|
69354
|
+
try {
|
|
69355
|
+
await client.ensureKpisTable(fs3.kpisTable);
|
|
69356
|
+
} catch {
|
|
69357
|
+
}
|
|
69358
|
+
}
|
|
68578
69359
|
let sessionSyncOk = true;
|
|
68579
69360
|
const memoryBootstrap = (async () => {
|
|
68580
69361
|
const sql = `SELECT path, size_bytes, mime_type FROM "${table}" ORDER BY path`;
|
|
@@ -68620,7 +69401,61 @@ var DeeplakeFs = class _DeeplakeFs {
|
|
|
68620
69401
|
} catch {
|
|
68621
69402
|
}
|
|
68622
69403
|
})() : Promise.resolve();
|
|
68623
|
-
|
|
69404
|
+
const goalsBootstrap = fs3.goalsTable ? (async () => {
|
|
69405
|
+
try {
|
|
69406
|
+
const goalRows = await client.query(
|
|
69407
|
+
// One row per goal_id (UPDATE-or-INSERT model). Synthesize
|
|
69408
|
+
// the canonical VFS path from owner / status / goal_id.
|
|
69409
|
+
`SELECT goal_id, owner, status, content, created_at FROM "${fs3.goalsTable}" ORDER BY created_at DESC`
|
|
69410
|
+
);
|
|
69411
|
+
for (const row of goalRows) {
|
|
69412
|
+
const owner = String(row["owner"] ?? "");
|
|
69413
|
+
const status = String(row["status"] ?? "");
|
|
69414
|
+
const goal_id = String(row["goal_id"] ?? "");
|
|
69415
|
+
if (!owner || !status || !goal_id)
|
|
69416
|
+
continue;
|
|
69417
|
+
if (status !== "opened" && status !== "in_progress" && status !== "closed")
|
|
69418
|
+
continue;
|
|
69419
|
+
const p22 = composeGoalPath({ owner, status, goal_id });
|
|
69420
|
+
const content = String(row["content"] ?? "");
|
|
69421
|
+
fs3.files.set(p22, Buffer.from(content, "utf-8"));
|
|
69422
|
+
fs3.meta.set(p22, {
|
|
69423
|
+
size: Buffer.byteLength(content, "utf-8"),
|
|
69424
|
+
mime: "text/markdown",
|
|
69425
|
+
mtime: /* @__PURE__ */ new Date()
|
|
69426
|
+
});
|
|
69427
|
+
fs3.addToTree(p22);
|
|
69428
|
+
fs3.flushed.add(p22);
|
|
69429
|
+
}
|
|
69430
|
+
} catch {
|
|
69431
|
+
}
|
|
69432
|
+
})() : Promise.resolve();
|
|
69433
|
+
const kpisBootstrap = fs3.kpisTable ? (async () => {
|
|
69434
|
+
try {
|
|
69435
|
+
const kpiRows = await client.query(
|
|
69436
|
+
// One row per (goal_id, kpi_id) (UPDATE-or-INSERT model).
|
|
69437
|
+
`SELECT goal_id, kpi_id, content, created_at FROM "${fs3.kpisTable}" ORDER BY created_at DESC`
|
|
69438
|
+
);
|
|
69439
|
+
for (const row of kpiRows) {
|
|
69440
|
+
const goal_id = String(row["goal_id"] ?? "");
|
|
69441
|
+
const kpi_id = String(row["kpi_id"] ?? "");
|
|
69442
|
+
if (!goal_id || !kpi_id)
|
|
69443
|
+
continue;
|
|
69444
|
+
const p22 = composeKpiPath({ goal_id, kpi_id });
|
|
69445
|
+
const content = String(row["content"] ?? "");
|
|
69446
|
+
fs3.files.set(p22, Buffer.from(content, "utf-8"));
|
|
69447
|
+
fs3.meta.set(p22, {
|
|
69448
|
+
size: Buffer.byteLength(content, "utf-8"),
|
|
69449
|
+
mime: "text/markdown",
|
|
69450
|
+
mtime: /* @__PURE__ */ new Date()
|
|
69451
|
+
});
|
|
69452
|
+
fs3.addToTree(p22);
|
|
69453
|
+
fs3.flushed.add(p22);
|
|
69454
|
+
}
|
|
69455
|
+
} catch {
|
|
69456
|
+
}
|
|
69457
|
+
})() : Promise.resolve();
|
|
69458
|
+
await Promise.all([memoryBootstrap, sessionsBootstrap, goalsBootstrap, kpisBootstrap]);
|
|
68624
69459
|
return fs3;
|
|
68625
69460
|
}
|
|
68626
69461
|
// ── tree management ───────────────────────────────────────────────────────
|
|
@@ -68639,7 +69474,7 @@ var DeeplakeFs = class _DeeplakeFs {
|
|
|
68639
69474
|
this.pending.delete(filePath);
|
|
68640
69475
|
this.flushed.delete(filePath);
|
|
68641
69476
|
const parent = parentOf(filePath);
|
|
68642
|
-
this.dirs.get(parent)?.delete(
|
|
69477
|
+
this.dirs.get(parent)?.delete(basename5(filePath));
|
|
68643
69478
|
}
|
|
68644
69479
|
// ── flush / write batching ────────────────────────────────────────────────
|
|
68645
69480
|
scheduleFlush() {
|
|
@@ -68689,6 +69524,15 @@ var DeeplakeFs = class _DeeplakeFs {
|
|
|
68689
69524
|
return Promise.all(rows.map((r10) => this.embedClient.embed(r10.contentText, "document")));
|
|
68690
69525
|
}
|
|
68691
69526
|
async upsertRow(r10, embedding) {
|
|
69527
|
+
const kind = classifyPath(r10.path);
|
|
69528
|
+
if (kind === "goal" && this.goalsTable) {
|
|
69529
|
+
await this.upsertGoalRow(r10);
|
|
69530
|
+
return;
|
|
69531
|
+
}
|
|
69532
|
+
if (kind === "kpi" && this.kpisTable) {
|
|
69533
|
+
await this.upsertKpiRow(r10);
|
|
69534
|
+
return;
|
|
69535
|
+
}
|
|
68692
69536
|
const text = sqlStr(r10.contentText);
|
|
68693
69537
|
const p22 = sqlStr(r10.path);
|
|
68694
69538
|
const fname = sqlStr(r10.filename);
|
|
@@ -68712,6 +69556,57 @@ var DeeplakeFs = class _DeeplakeFs {
|
|
|
68712
69556
|
this.flushed.add(r10.path);
|
|
68713
69557
|
}
|
|
68714
69558
|
}
|
|
69559
|
+
/**
|
|
69560
|
+
* UPDATE-or-INSERT for a goal row, keyed by goal_id. One row per
|
|
69561
|
+
* goal forever — status changes, owner reassignments, and text
|
|
69562
|
+
* edits all mutate the same row in place. The version column
|
|
69563
|
+
* stays at 1 (vestigial in the schema; kept so the column is
|
|
69564
|
+
* already there if we ever bring back the audit-trail pattern).
|
|
69565
|
+
*
|
|
69566
|
+
* Trade-off versus the prior INSERT-only-version-bump design:
|
|
69567
|
+
* - Pros: 1 row per goal makes the Deeplake table view obvious,
|
|
69568
|
+
* no row proliferation, simpler bootstrap queries.
|
|
69569
|
+
* - Cons: no audit trail; vulnerable to Deeplake's
|
|
69570
|
+
* UPDATE-coalescing quirk if two writes hit the same row
|
|
69571
|
+
* within microseconds. For the v1 single-user / small-team
|
|
69572
|
+
* workflow the user explicitly chose this trade-off.
|
|
69573
|
+
*/
|
|
69574
|
+
async upsertGoalRow(r10) {
|
|
69575
|
+
if (!this.goalsTable)
|
|
69576
|
+
throw new Error("goalsTable not configured");
|
|
69577
|
+
const parts = decomposeGoalPath(r10.path);
|
|
69578
|
+
const safe = this.goalsTable;
|
|
69579
|
+
const ts3 = r10.lastUpdateDate ?? r10.creationDate ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
69580
|
+
const existing = await this.client.query(`SELECT id FROM "${safe}" WHERE goal_id = '${sqlStr(parts.goal_id)}' LIMIT 1`);
|
|
69581
|
+
if (existing.length > 0) {
|
|
69582
|
+
await this.client.query(`UPDATE "${safe}" SET owner = '${sqlStr(parts.owner)}', status = '${sqlStr(parts.status)}', content = E'${sqlStr(r10.contentText)}', created_at = '${sqlStr(ts3)}' WHERE goal_id = '${sqlStr(parts.goal_id)}'`);
|
|
69583
|
+
} else {
|
|
69584
|
+
const id = randomUUID2();
|
|
69585
|
+
await this.client.query(`INSERT INTO "${safe}" (id, goal_id, owner, status, content, version, created_at, agent, plugin_version) VALUES ('${id}', '${sqlStr(parts.goal_id)}', '${sqlStr(parts.owner)}', '${sqlStr(parts.status)}', E'${sqlStr(r10.contentText)}', 1, '${sqlStr(ts3)}', 'manual', '')`);
|
|
69586
|
+
}
|
|
69587
|
+
this.flushed.add(r10.path);
|
|
69588
|
+
}
|
|
69589
|
+
/**
|
|
69590
|
+
* UPDATE-or-INSERT for a KPI row, keyed by (goal_id, kpi_id).
|
|
69591
|
+
* Same trade-off as upsertGoalRow — one row per KPI forever,
|
|
69592
|
+
* no version proliferation. Progress bumps (Edit on the `current:`
|
|
69593
|
+
* line) and any other content edits mutate the same row in place.
|
|
69594
|
+
*/
|
|
69595
|
+
async upsertKpiRow(r10) {
|
|
69596
|
+
if (!this.kpisTable)
|
|
69597
|
+
throw new Error("kpisTable not configured");
|
|
69598
|
+
const parts = decomposeKpiPath(r10.path);
|
|
69599
|
+
const safe = this.kpisTable;
|
|
69600
|
+
const ts3 = r10.lastUpdateDate ?? r10.creationDate ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
69601
|
+
const existing = await this.client.query(`SELECT id FROM "${safe}" WHERE goal_id = '${sqlStr(parts.goal_id)}' AND kpi_id = '${sqlStr(parts.kpi_id)}' LIMIT 1`);
|
|
69602
|
+
if (existing.length > 0) {
|
|
69603
|
+
await this.client.query(`UPDATE "${safe}" SET content = E'${sqlStr(r10.contentText)}', created_at = '${sqlStr(ts3)}' WHERE goal_id = '${sqlStr(parts.goal_id)}' AND kpi_id = '${sqlStr(parts.kpi_id)}'`);
|
|
69604
|
+
} else {
|
|
69605
|
+
const id = randomUUID2();
|
|
69606
|
+
await this.client.query(`INSERT INTO "${safe}" (id, goal_id, kpi_id, content, version, created_at, agent, plugin_version) VALUES ('${id}', '${sqlStr(parts.goal_id)}', '${sqlStr(parts.kpi_id)}', E'${sqlStr(r10.contentText)}', 1, '${sqlStr(ts3)}', 'manual', '')`);
|
|
69607
|
+
}
|
|
69608
|
+
this.flushed.add(r10.path);
|
|
69609
|
+
}
|
|
68715
69610
|
// ── Virtual index.md generation ────────────────────────────────────────────
|
|
68716
69611
|
async generateVirtualIndex() {
|
|
68717
69612
|
const fetchLimit = INDEX_LIMIT_PER_SECTION + 1;
|
|
@@ -68813,6 +69708,11 @@ var DeeplakeFs = class _DeeplakeFs {
|
|
|
68813
69708
|
}
|
|
68814
69709
|
async readFile(path2, _opts) {
|
|
68815
69710
|
const p22 = normPath(path2);
|
|
69711
|
+
if (isGraphPath(p22)) {
|
|
69712
|
+
if (isGraphDir(p22))
|
|
69713
|
+
throw fsErr("EISDIR", "illegal operation on a directory", p22);
|
|
69714
|
+
return readGraphFile(p22, process.cwd());
|
|
69715
|
+
}
|
|
68816
69716
|
if (this.dirs.has(p22) && !this.files.has(p22))
|
|
68817
69717
|
throw fsErr("EISDIR", "illegal operation on a directory", p22);
|
|
68818
69718
|
if (p22 === "/index.md" && !this.files.has(p22)) {
|
|
@@ -68860,13 +69760,13 @@ var DeeplakeFs = class _DeeplakeFs {
|
|
|
68860
69760
|
throw fsErr("EISDIR", "illegal operation on a directory", p22);
|
|
68861
69761
|
const text = typeof content === "string" ? content : Buffer.from(content).toString("utf-8");
|
|
68862
69762
|
const buf = Buffer.from(text, "utf-8");
|
|
68863
|
-
const mime = guessMime(
|
|
69763
|
+
const mime = guessMime(basename5(p22));
|
|
68864
69764
|
this.files.set(p22, buf);
|
|
68865
69765
|
this.meta.set(p22, { size: buf.length, mime, mtime: /* @__PURE__ */ new Date() });
|
|
68866
69766
|
this.addToTree(p22);
|
|
68867
69767
|
this.pending.set(p22, {
|
|
68868
69768
|
path: p22,
|
|
68869
|
-
filename:
|
|
69769
|
+
filename: basename5(p22),
|
|
68870
69770
|
contentText: text,
|
|
68871
69771
|
mimeType: mime,
|
|
68872
69772
|
sizeBytes: buf.length,
|
|
@@ -68885,13 +69785,13 @@ var DeeplakeFs = class _DeeplakeFs {
|
|
|
68885
69785
|
throw fsErr("EISDIR", "illegal operation on a directory", p22);
|
|
68886
69786
|
const text = typeof content === "string" ? content : Buffer.from(content).toString("utf-8");
|
|
68887
69787
|
const buf = Buffer.from(text, "utf-8");
|
|
68888
|
-
const mime = guessMime(
|
|
69788
|
+
const mime = guessMime(basename5(p22));
|
|
68889
69789
|
this.files.set(p22, buf);
|
|
68890
69790
|
this.meta.set(p22, { size: buf.length, mime, mtime: /* @__PURE__ */ new Date() });
|
|
68891
69791
|
this.addToTree(p22);
|
|
68892
69792
|
this.pending.set(p22, {
|
|
68893
69793
|
path: p22,
|
|
68894
|
-
filename:
|
|
69794
|
+
filename: basename5(p22),
|
|
68895
69795
|
contentText: text,
|
|
68896
69796
|
mimeType: mime,
|
|
68897
69797
|
sizeBytes: buf.length
|
|
@@ -68925,10 +69825,33 @@ var DeeplakeFs = class _DeeplakeFs {
|
|
|
68925
69825
|
const p22 = normPath(path2);
|
|
68926
69826
|
if (p22 === "/index.md")
|
|
68927
69827
|
return true;
|
|
69828
|
+
if (isGraphPath(p22)) {
|
|
69829
|
+
if (isGraphDir(p22))
|
|
69830
|
+
return true;
|
|
69831
|
+
const r10 = handleGraphVfs(graphSubpathOf(p22), process.cwd());
|
|
69832
|
+
return r10.kind === "ok" || r10.kind === "no-graph";
|
|
69833
|
+
}
|
|
68928
69834
|
return this.files.has(p22) || this.dirs.has(p22);
|
|
68929
69835
|
}
|
|
68930
69836
|
async stat(path2) {
|
|
68931
69837
|
const p22 = normPath(path2);
|
|
69838
|
+
if (isGraphPath(p22)) {
|
|
69839
|
+
if (!isGraphDir(p22)) {
|
|
69840
|
+
const r10 = handleGraphVfs(graphSubpathOf(p22), process.cwd());
|
|
69841
|
+
if (r10.kind === "not-found")
|
|
69842
|
+
throw fsErr("ENOENT", "no such file or directory", p22);
|
|
69843
|
+
}
|
|
69844
|
+
const dir = isGraphDir(p22);
|
|
69845
|
+
return {
|
|
69846
|
+
isFile: !dir,
|
|
69847
|
+
isDirectory: dir,
|
|
69848
|
+
isSymbolicLink: false,
|
|
69849
|
+
mode: dir ? 493 : 420,
|
|
69850
|
+
size: 0,
|
|
69851
|
+
// synthesized; cheaper than computing the body just to size it
|
|
69852
|
+
mtime: /* @__PURE__ */ new Date()
|
|
69853
|
+
};
|
|
69854
|
+
}
|
|
68932
69855
|
const isFile = this.files.has(p22);
|
|
68933
69856
|
const isDir = this.dirs.has(p22);
|
|
68934
69857
|
if (p22 === "/index.md" && !isFile && !isDir) {
|
|
@@ -68973,6 +69896,14 @@ var DeeplakeFs = class _DeeplakeFs {
|
|
|
68973
69896
|
const p22 = normPath(path2);
|
|
68974
69897
|
if (p22 === "/index.md")
|
|
68975
69898
|
return p22;
|
|
69899
|
+
if (isGraphPath(p22)) {
|
|
69900
|
+
if (isGraphDir(p22))
|
|
69901
|
+
return p22;
|
|
69902
|
+
const r10 = handleGraphVfs(graphSubpathOf(p22), process.cwd());
|
|
69903
|
+
if (r10.kind === "ok" || r10.kind === "no-graph")
|
|
69904
|
+
return p22;
|
|
69905
|
+
throw fsErr("ENOENT", "no such file or directory", p22);
|
|
69906
|
+
}
|
|
68976
69907
|
if (!this.files.has(p22) && !this.dirs.has(p22))
|
|
68977
69908
|
throw fsErr("ENOENT", "no such file or directory", p22);
|
|
68978
69909
|
return p22;
|
|
@@ -68996,16 +69927,24 @@ var DeeplakeFs = class _DeeplakeFs {
|
|
|
68996
69927
|
const parent = parentOf(p22);
|
|
68997
69928
|
if (!this.dirs.has(parent))
|
|
68998
69929
|
this.dirs.set(parent, /* @__PURE__ */ new Set());
|
|
68999
|
-
this.dirs.get(parent).add(
|
|
69930
|
+
this.dirs.get(parent).add(basename5(p22));
|
|
69000
69931
|
}
|
|
69001
69932
|
async readdir(path2) {
|
|
69002
69933
|
const p22 = normPath(path2);
|
|
69934
|
+
if (p22 === GRAPH_ROOT)
|
|
69935
|
+
return ["index.md", "find", "show"];
|
|
69936
|
+
if (p22 === "/graph/find" || p22 === "/graph/show") {
|
|
69937
|
+
return [];
|
|
69938
|
+
}
|
|
69003
69939
|
if (!this.dirs.has(p22))
|
|
69004
69940
|
throw fsErr("ENOTDIR", "not a directory", p22);
|
|
69005
69941
|
const entries = [...this.dirs.get(p22) ?? []];
|
|
69006
69942
|
if (p22 === "/" && !entries.includes("index.md")) {
|
|
69007
69943
|
entries.push("index.md");
|
|
69008
69944
|
}
|
|
69945
|
+
if (p22 === "/" && !entries.includes("graph")) {
|
|
69946
|
+
entries.push("graph");
|
|
69947
|
+
}
|
|
69009
69948
|
return entries;
|
|
69010
69949
|
}
|
|
69011
69950
|
async readdirWithFileTypes(path2) {
|
|
@@ -69013,6 +69952,14 @@ var DeeplakeFs = class _DeeplakeFs {
|
|
|
69013
69952
|
const p22 = normPath(path2);
|
|
69014
69953
|
return names.map((name) => {
|
|
69015
69954
|
const child = p22 === "/" ? `/${name}` : `${p22}/${name}`;
|
|
69955
|
+
if (isGraphPath(child)) {
|
|
69956
|
+
return {
|
|
69957
|
+
name,
|
|
69958
|
+
isFile: !isGraphDir(child),
|
|
69959
|
+
isDirectory: isGraphDir(child),
|
|
69960
|
+
isSymbolicLink: false
|
|
69961
|
+
};
|
|
69962
|
+
}
|
|
69016
69963
|
return {
|
|
69017
69964
|
name,
|
|
69018
69965
|
isFile: (this.files.has(child) || child === "/index.md") && !this.dirs.has(child),
|
|
@@ -69031,6 +69978,31 @@ var DeeplakeFs = class _DeeplakeFs {
|
|
|
69031
69978
|
return;
|
|
69032
69979
|
throw fsErr("ENOENT", "no such file or directory", p22);
|
|
69033
69980
|
}
|
|
69981
|
+
if (this.goalsTable && classifyPath(p22) === "goal") {
|
|
69982
|
+
const parts = decomposeGoalPath(p22);
|
|
69983
|
+
if (parts.status === "closed") {
|
|
69984
|
+
this.removeFromTree(p22);
|
|
69985
|
+
return;
|
|
69986
|
+
}
|
|
69987
|
+
const closedPath = composeGoalPath({ ...parts, status: "closed" });
|
|
69988
|
+
const contentBuf = this.files.get(p22);
|
|
69989
|
+
const content = contentBuf instanceof Buffer ? contentBuf.toString("utf-8") : "";
|
|
69990
|
+
await this.upsertGoalRow({
|
|
69991
|
+
path: closedPath,
|
|
69992
|
+
filename: basename5(closedPath),
|
|
69993
|
+
contentText: content,
|
|
69994
|
+
mimeType: "text/markdown",
|
|
69995
|
+
sizeBytes: Buffer.byteLength(content, "utf-8"),
|
|
69996
|
+
creationDate: void 0,
|
|
69997
|
+
lastUpdateDate: (/* @__PURE__ */ new Date()).toISOString()
|
|
69998
|
+
});
|
|
69999
|
+
this.files.set(closedPath, contentBuf ?? null);
|
|
70000
|
+
this.meta.set(closedPath, this.meta.get(p22) ?? { size: 0, mime: "text/markdown", mtime: /* @__PURE__ */ new Date() });
|
|
70001
|
+
this.addToTree(closedPath);
|
|
70002
|
+
this.flushed.add(closedPath);
|
|
70003
|
+
this.removeFromTree(p22);
|
|
70004
|
+
return;
|
|
70005
|
+
}
|
|
69034
70006
|
if (this.dirs.has(p22)) {
|
|
69035
70007
|
const children = this.dirs.get(p22) ?? /* @__PURE__ */ new Set();
|
|
69036
70008
|
if (children.size > 0 && !opts?.recursive)
|
|
@@ -69051,7 +70023,7 @@ var DeeplakeFs = class _DeeplakeFs {
|
|
|
69051
70023
|
for (const fp of safeToDelete)
|
|
69052
70024
|
this.removeFromTree(fp);
|
|
69053
70025
|
this.dirs.delete(p22);
|
|
69054
|
-
this.dirs.get(parentOf(p22))?.delete(
|
|
70026
|
+
this.dirs.get(parentOf(p22))?.delete(basename5(p22));
|
|
69055
70027
|
if (safeToDelete.length > 0) {
|
|
69056
70028
|
const inList = safeToDelete.map((fp) => `'${sqlStr(fp)}'`).join(", ");
|
|
69057
70029
|
await this.client.query(`DELETE FROM "${this.table}" WHERE path IN (${inList})`);
|
|
@@ -69081,6 +70053,32 @@ var DeeplakeFs = class _DeeplakeFs {
|
|
|
69081
70053
|
throw fsErr("EPERM", "session files are read-only", s10);
|
|
69082
70054
|
if (this.sessionPaths.has(d15))
|
|
69083
70055
|
throw fsErr("EPERM", "session files are read-only", d15);
|
|
70056
|
+
if (this.goalsTable && classifyPath(s10) === "goal" && classifyPath(d15) === "goal") {
|
|
70057
|
+
const from = decomposeGoalPath(s10);
|
|
70058
|
+
const to3 = decomposeGoalPath(d15);
|
|
70059
|
+
if (from.goal_id !== to3.goal_id || from.owner !== to3.owner) {
|
|
70060
|
+
throw fsErr("EPERM", "cannot rename goal_id or owner via mv (only status)", d15);
|
|
70061
|
+
}
|
|
70062
|
+
if (!this.files.has(s10))
|
|
70063
|
+
throw fsErr("ENOENT", "no such file or directory", s10);
|
|
70064
|
+
const contentBuf = this.files.get(s10);
|
|
70065
|
+
const content = contentBuf instanceof Buffer ? contentBuf.toString("utf-8") : "";
|
|
70066
|
+
await this.upsertGoalRow({
|
|
70067
|
+
path: d15,
|
|
70068
|
+
filename: basename5(d15),
|
|
70069
|
+
contentText: content,
|
|
70070
|
+
mimeType: "text/markdown",
|
|
70071
|
+
sizeBytes: Buffer.byteLength(content, "utf-8"),
|
|
70072
|
+
creationDate: void 0,
|
|
70073
|
+
lastUpdateDate: (/* @__PURE__ */ new Date()).toISOString()
|
|
70074
|
+
});
|
|
70075
|
+
this.files.set(d15, contentBuf ?? null);
|
|
70076
|
+
this.meta.set(d15, this.meta.get(s10) ?? { size: 0, mime: "text/markdown", mtime: /* @__PURE__ */ new Date() });
|
|
70077
|
+
this.addToTree(d15);
|
|
70078
|
+
this.flushed.add(d15);
|
|
70079
|
+
this.removeFromTree(s10);
|
|
70080
|
+
return;
|
|
70081
|
+
}
|
|
69084
70082
|
await this.cp(src, dest, { recursive: true });
|
|
69085
70083
|
await this.rm(src, { recursive: true, force: true });
|
|
69086
70084
|
}
|
|
@@ -69096,7 +70094,7 @@ var DeeplakeFs = class _DeeplakeFs {
|
|
|
69096
70094
|
|
|
69097
70095
|
// node_modules/yargs-parser/build/lib/index.js
|
|
69098
70096
|
import { format } from "util";
|
|
69099
|
-
import { normalize, resolve as
|
|
70097
|
+
import { normalize, resolve as resolve6 } from "path";
|
|
69100
70098
|
|
|
69101
70099
|
// node_modules/yargs-parser/build/lib/string-utils.js
|
|
69102
70100
|
function camelCase2(str) {
|
|
@@ -70034,7 +71032,7 @@ function stripQuotes(val) {
|
|
|
70034
71032
|
}
|
|
70035
71033
|
|
|
70036
71034
|
// node_modules/yargs-parser/build/lib/index.js
|
|
70037
|
-
import { readFileSync as
|
|
71035
|
+
import { readFileSync as readFileSync10 } from "fs";
|
|
70038
71036
|
import { createRequire as createRequire2 } from "node:module";
|
|
70039
71037
|
var _a3;
|
|
70040
71038
|
var _b;
|
|
@@ -70056,12 +71054,12 @@ var parser = new YargsParser({
|
|
|
70056
71054
|
},
|
|
70057
71055
|
format,
|
|
70058
71056
|
normalize,
|
|
70059
|
-
resolve:
|
|
71057
|
+
resolve: resolve6,
|
|
70060
71058
|
require: (path2) => {
|
|
70061
71059
|
if (typeof require2 !== "undefined") {
|
|
70062
71060
|
return require2(path2);
|
|
70063
71061
|
} else if (path2.match(/\.json$/)) {
|
|
70064
|
-
return JSON.parse(
|
|
71062
|
+
return JSON.parse(readFileSync10(path2, "utf8"));
|
|
70065
71063
|
} else {
|
|
70066
71064
|
throw Error("only .json config files are supported in ESM");
|
|
70067
71065
|
}
|
|
@@ -70081,11 +71079,11 @@ var lib_default = yargsParser;
|
|
|
70081
71079
|
|
|
70082
71080
|
// dist/src/shell/grep-interceptor.js
|
|
70083
71081
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
70084
|
-
import { dirname as
|
|
71082
|
+
import { dirname as dirname10, join as join17 } from "node:path";
|
|
70085
71083
|
var SEMANTIC_SEARCH_ENABLED = process.env.HIVEMIND_SEMANTIC_SEARCH !== "false" && !embeddingsDisabled();
|
|
70086
71084
|
var SEMANTIC_EMBED_TIMEOUT_MS = Number(process.env.HIVEMIND_SEMANTIC_EMBED_TIMEOUT_MS ?? "500");
|
|
70087
71085
|
function resolveGrepEmbedDaemonPath() {
|
|
70088
|
-
return
|
|
71086
|
+
return join17(dirname10(fileURLToPath2(import.meta.url)), "..", "embeddings", "embed-daemon.js");
|
|
70089
71087
|
}
|
|
70090
71088
|
var sharedGrepEmbedClient = null;
|
|
70091
71089
|
function getGrepEmbedClient() {
|
|
@@ -70235,13 +71233,15 @@ async function main() {
|
|
|
70235
71233
|
}
|
|
70236
71234
|
const table = process.env["HIVEMIND_TABLE"] ?? "memory";
|
|
70237
71235
|
const sessionsTable = process.env["HIVEMIND_SESSIONS_TABLE"] ?? "sessions";
|
|
71236
|
+
const goalsTable = process.env["HIVEMIND_GOALS_TABLE"] ?? config.goalsTableName;
|
|
71237
|
+
const kpisTable = process.env["HIVEMIND_KPIS_TABLE"] ?? config.kpisTableName;
|
|
70238
71238
|
const mount = process.env["HIVEMIND_MOUNT"] ?? "/";
|
|
70239
71239
|
const client = new DeeplakeApi(config.token, config.apiUrl, config.orgId, config.workspaceId, table);
|
|
70240
71240
|
if (!isOneShot) {
|
|
70241
71241
|
process.stderr.write(`Connecting to deeplake://${config.workspaceId}/${table} ...
|
|
70242
71242
|
`);
|
|
70243
71243
|
}
|
|
70244
|
-
const fs3 = await DeeplakeFs.create(client, table, mount, sessionsTable);
|
|
71244
|
+
const fs3 = await DeeplakeFs.create(client, table, mount, sessionsTable, { goalsTable, kpisTable });
|
|
70245
71245
|
if (!isOneShot) {
|
|
70246
71246
|
const fileCount = fs3.getAllPaths().filter((p22) => !!p22).length;
|
|
70247
71247
|
process.stderr.write(`Ready. ${fileCount} files loaded.
|