@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.
Files changed (39) hide show
  1. package/.claude-plugin/marketplace.json +3 -3
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/README.md +64 -0
  4. package/bundle/cli.js +22702 -7775
  5. package/codex/bundle/capture.js +228 -0
  6. package/codex/bundle/commands/auth-login.js +228 -0
  7. package/codex/bundle/graph-pull-worker.js +1370 -0
  8. package/codex/bundle/pre-tool-use.js +228 -0
  9. package/codex/bundle/session-start-setup.js +228 -0
  10. package/codex/bundle/session-start.js +255 -4
  11. package/codex/bundle/shell/deeplake-shell.js +1028 -28
  12. package/codex/bundle/skillify-worker.js +94 -3
  13. package/codex/bundle/stop.js +282 -50
  14. package/codex/skills/hivemind-goals/SKILL.md +157 -0
  15. package/cursor/bundle/capture.js +282 -50
  16. package/cursor/bundle/commands/auth-login.js +228 -0
  17. package/cursor/bundle/graph-pull-worker.js +1370 -0
  18. package/cursor/bundle/pre-tool-use.js +228 -0
  19. package/cursor/bundle/session-end.js +65 -44
  20. package/cursor/bundle/session-start.js +662 -6
  21. package/cursor/bundle/shell/deeplake-shell.js +1028 -28
  22. package/cursor/bundle/skillify-worker.js +94 -3
  23. package/hermes/bundle/capture.js +282 -50
  24. package/hermes/bundle/commands/auth-login.js +228 -0
  25. package/hermes/bundle/graph-pull-worker.js +1370 -0
  26. package/hermes/bundle/pre-tool-use.js +228 -0
  27. package/hermes/bundle/session-end.js +65 -44
  28. package/hermes/bundle/session-start.js +662 -6
  29. package/hermes/bundle/shell/deeplake-shell.js +1028 -28
  30. package/hermes/bundle/skillify-worker.js +94 -3
  31. package/mcp/bundle/server.js +228 -0
  32. package/openclaw/dist/chunks/config-FH6JYSJW.js +53 -0
  33. package/openclaw/dist/index.js +307 -2
  34. package/openclaw/dist/skillify-worker.js +94 -3
  35. package/openclaw/openclaw.plugin.json +4 -2
  36. package/openclaw/package.json +1 -1
  37. package/openclaw/skills/hivemind-goals/SKILL.md +30 -0
  38. package/package.json +4 -1
  39. 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 join14(output, replacement);
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 = join14(output, rule.append(self2.options));
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 join14(output, replacement) {
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((resolve6) => setTimeout(resolve6, ms3));
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((resolve6) => this.waiting.push(resolve6));
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 basename4, posix } from "node:path";
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 dirname5, join as join12 } from "node:path";
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((resolve6, reject) => {
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
- resolve6(sock);
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((resolve6, reject) => {
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
- resolve6(JSON.parse(line));
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 join12(dirname5(fileURLToPath(import.meta.url)), "..", "embeddings", "embed-daemon.js");
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
- await Promise.all([memoryBootstrap, sessionsBootstrap]);
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(basename4(filePath));
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(basename4(p22));
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: basename4(p22),
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(basename4(p22));
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: basename4(p22),
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(basename4(p22));
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(basename4(p22));
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 resolve5 } from "path";
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 readFileSync7 } from "fs";
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: resolve5,
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(readFileSync7(path2, "utf8"));
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 dirname6, join as join13 } from "node:path";
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 join13(dirname6(fileURLToPath2(import.meta.url)), "..", "embeddings", "embed-daemon.js");
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.