musememory 1.9.0 → 1.10.0

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 (3) hide show
  1. package/README.md +1 -0
  2. package/dist/index.js +474 -154
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -431,6 +431,7 @@ memory <command> [arguments] [flags] # alias: musememory
431
431
  | `context` | `[query] [--token-budget N] [--limit N]` | Retrieve Top-$K$ ranked active context for prompt injection. |
432
432
  | `search` | `<query> [--limit N] [--token-budget N] [--type T]` | Ranked multi-factor token search with score breakdown. |
433
433
  | `search-transcript` | `<query> [file.jsonl] [--window N]` | Full-text search past transcripts with dialogue context windows. |
434
+ | `learn` / `sync-chats` | `[--confirm] [--force] [--max N]` | **Universal auto-learner**: auto-discovers and distills memories from all agent chats across host machine. |
434
435
  | `harvest` | `<text\|file> --project P [--confirmed]` | Distill conversation turns into structured fix/decision memory units. |
435
436
  | `import-transcript` | `<file.jsonl> [--project P] [--confirmed]` | Ingest raw `.jsonl` session transcripts from coding agents. |
436
437
  | `capture` / `propose` | `<text> --project P [--title T] [--type T]` | Propose a memory entry with inline Vibeguard secret inspection. |
package/dist/index.js CHANGED
@@ -4857,7 +4857,24 @@ function extractHarvestUnits(text) {
4857
4857
  continue;
4858
4858
  }
4859
4859
  const speakerStripped = trimmed.replace(/^(?:user|assistant|human|system|agent|client|developer)\s*[:\-\u2013]\s*/i, "");
4860
- const typeMatch = speakerStripped.match(/^(?:#{1,6}\s+|[-*]\s+)?\*{0,2}(fix|failure|decision|architecture|constraint|operation|preference|discovery|outcome|root cause|learned|rule)\*{0,2}\s*[:\-\u2013]\s*(.*)$/i);
4860
+ const typeMatch = speakerStripped.match(/^(?:#{1,6}\s+|[-*]\s+)?\*{0,2}(fix|failure|decision|architecture|constraint|operation|preference|discovery|outcome|root cause|learned|rule|solution|resolution|workaround|hard rule|policy)\*{0,2}\s*[:\-\u2013]\s*(.*)$/i);
4861
+ let naturalType = null;
4862
+ let naturalContent = "";
4863
+ if (!typeMatch) {
4864
+ if (/\b(?:resolved|fixed|patched|worked around)\s+(?:the\s+)?([^.\n]+)/i.test(speakerStripped)) {
4865
+ naturalType = "fix";
4866
+ naturalContent = speakerStripped;
4867
+ } else if (/\b(?:decided|chose|selected|agreed)\s+to\s+([^.\n]+)/i.test(speakerStripped)) {
4868
+ naturalType = "decision";
4869
+ naturalContent = speakerStripped;
4870
+ } else if (/\b(?:must\s+always|must\s+never|never\s+commit|you\s+must\s+never|do\s+not\s+allow)\s+([^.\n]+)/i.test(speakerStripped)) {
4871
+ naturalType = "constraint";
4872
+ naturalContent = speakerStripped;
4873
+ } else if (/\b(?:user\s+prefers|prefers\s+using|prefers\s+to)\s+([^.\n]+)/i.test(speakerStripped)) {
4874
+ naturalType = "preference";
4875
+ naturalContent = speakerStripped;
4876
+ }
4877
+ }
4861
4878
  if (typeMatch) {
4862
4879
  flush();
4863
4880
  const rawTag = typeMatch[1].toLowerCase();
@@ -4866,6 +4883,11 @@ function extractHarvestUnits(text) {
4866
4883
  if (currentTitle) {
4867
4884
  currentBuffer.push(currentTitle);
4868
4885
  }
4886
+ } else if (naturalType && naturalContent) {
4887
+ flush();
4888
+ currentType = naturalType;
4889
+ currentTitle = naturalContent.slice(0, 80);
4890
+ currentBuffer.push(naturalContent);
4869
4891
  } else if (trimmed.match(/^(?:user|assistant|human|system|agent|client|developer)\s*[:\-\u2013]/i)) {
4870
4892
  flush();
4871
4893
  } else if (currentType !== null) {
@@ -4879,16 +4901,21 @@ function normalizeHarvestType(raw) {
4879
4901
  switch (raw) {
4880
4902
  case "fix":
4881
4903
  case "root cause":
4904
+ case "solution":
4905
+ case "resolution":
4906
+ case "workaround":
4882
4907
  return "fix";
4883
4908
  case "failure":
4884
4909
  return "failure";
4885
4910
  case "decision":
4886
4911
  case "outcome":
4887
4912
  case "rule":
4913
+ case "policy":
4888
4914
  return "decision";
4889
4915
  case "architecture":
4890
4916
  return "architecture";
4891
4917
  case "constraint":
4918
+ case "hard rule":
4892
4919
  return "constraint";
4893
4920
  case "operation":
4894
4921
  return "operation";
@@ -15746,19 +15773,265 @@ var init_schema = __esm(() => {
15746
15773
  import_ajv = __toESM(require_ajv(), 1);
15747
15774
  });
15748
15775
 
15776
+ // src/harvester.ts
15777
+ var exports_harvester = {};
15778
+ __export(exports_harvester, {
15779
+ discoverAgentTranscripts: () => discoverAgentTranscripts,
15780
+ getHarvestedTranscriptLedger: () => getHarvestedTranscriptLedger,
15781
+ getStandardAgentTranscriptDirs: () => getStandardAgentTranscriptDirs,
15782
+ handleLearnCommand: () => handleLearnCommand,
15783
+ harvestAllAgentTranscripts: () => harvestAllAgentTranscripts,
15784
+ saveHarvestedTranscriptLedger: () => saveHarvestedTranscriptLedger
15785
+ });
15786
+ import {
15787
+ existsSync as existsSync24,
15788
+ readFileSync as readFileSync20,
15789
+ writeFileSync as writeFileSync12,
15790
+ readdirSync as readdirSync8,
15791
+ statSync as statSync8,
15792
+ mkdirSync as mkdirSync14,
15793
+ openSync,
15794
+ readSync,
15795
+ closeSync
15796
+ } from "node:fs";
15797
+ import { join as join21, dirname as dirname5 } from "node:path";
15798
+ import { homedir as homedir5 } from "node:os";
15799
+ import { createHash as createHash2 } from "node:crypto";
15800
+ function getStandardAgentTranscriptDirs(home = homedir5()) {
15801
+ return [
15802
+ join21(home, ".gemini", "antigravity-cli", "brain"),
15803
+ join21(home, ".config", "opencode", "transcripts"),
15804
+ join21(home, ".local", "share", "opencode", "transcripts"),
15805
+ join21(home, ".claude", "transcripts"),
15806
+ join21(home, ".claude", "projects"),
15807
+ join21(home, ".config", "LIFEOS", "runtime", "transcripts"),
15808
+ join21(home, ".hermes", "sessions"),
15809
+ join21(home, ".hermes", "transcripts"),
15810
+ join21(home, ".config", "goose", "sessions"),
15811
+ join21(home, ".codex", "sessions"),
15812
+ join21(home, ".config", "Cursor", "User", "workspaceStorage"),
15813
+ join21(process.cwd(), ".memory", "inbox"),
15814
+ join21(process.cwd(), ".logs")
15815
+ ];
15816
+ }
15817
+ function findJsonlFilesInDir(dir, maxDepth = 4, currentDepth = 0, out = []) {
15818
+ if (currentDepth > maxDepth || !existsSync24(dir))
15819
+ return out;
15820
+ let entries;
15821
+ try {
15822
+ entries = readdirSync8(dir, { withFileTypes: true });
15823
+ } catch {
15824
+ return out;
15825
+ }
15826
+ for (const e of entries) {
15827
+ if (e.name === "node_modules" || e.name === ".git" || e.name === "dist" || e.name === "processed" || e.name === "chunks" || e.name === ".cache" || e.name === ".tmp" || e.name.startsWith("tmp")) {
15828
+ continue;
15829
+ }
15830
+ const full = join21(dir, e.name);
15831
+ if (e.isDirectory()) {
15832
+ findJsonlFilesInDir(full, maxDepth, currentDepth + 1, out);
15833
+ } else if (e.isFile() && e.name.endsWith(".jsonl")) {
15834
+ if (!e.name.match(/^\d{8}\.jsonl$/) && e.name !== "transcript_full.jsonl") {
15835
+ out.push(full);
15836
+ }
15837
+ }
15838
+ }
15839
+ return out;
15840
+ }
15841
+ function discoverAgentTranscripts(options = {}) {
15842
+ const dirs = options.searchRoots && options.searchRoots.length > 0 ? options.searchRoots : getStandardAgentTranscriptDirs();
15843
+ const found = new Set;
15844
+ for (const d of dirs) {
15845
+ if (existsSync24(d)) {
15846
+ try {
15847
+ const stat = statSync8(d);
15848
+ if (stat.isDirectory()) {
15849
+ const files = findJsonlFilesInDir(d);
15850
+ for (const f of files)
15851
+ found.add(f);
15852
+ } else if (stat.isFile() && d.endsWith(".jsonl")) {
15853
+ found.add(d);
15854
+ }
15855
+ } catch {}
15856
+ }
15857
+ }
15858
+ const sorted = Array.from(found).sort((a, b) => {
15859
+ try {
15860
+ return statSync8(b).mtimeMs - statSync8(a).mtimeMs;
15861
+ } catch {
15862
+ return 0;
15863
+ }
15864
+ });
15865
+ if (options.maxFiles && options.maxFiles > 0) {
15866
+ return sorted.slice(0, options.maxFiles);
15867
+ }
15868
+ return sorted;
15869
+ }
15870
+ function getHarvestedTranscriptLedger(memoryDir) {
15871
+ const ledgerPath = join21(memoryDir, "harvested-transcripts.json");
15872
+ if (!existsSync24(ledgerPath))
15873
+ return {};
15874
+ try {
15875
+ const raw = readFileSync20(ledgerPath, "utf8");
15876
+ return JSON.parse(raw);
15877
+ } catch {
15878
+ return {};
15879
+ }
15880
+ }
15881
+ function saveHarvestedTranscriptLedger(memoryDir, ledger) {
15882
+ const ledgerPath = join21(memoryDir, "harvested-transcripts.json");
15883
+ const dir = dirname5(ledgerPath);
15884
+ if (!existsSync24(dir))
15885
+ mkdirSync14(dir, { recursive: true });
15886
+ writeFileSync12(ledgerPath, JSON.stringify(ledger, null, 2) + `
15887
+ `, "utf8");
15888
+ }
15889
+ function harvestAllAgentTranscripts(store, options = {}) {
15890
+ const memoryDir = options.memoryDir || join21(process.cwd(), ".memory");
15891
+ const ledger = getHarvestedTranscriptLedger(memoryDir);
15892
+ const files = discoverAgentTranscripts({
15893
+ searchRoots: options.searchRoots,
15894
+ maxFiles: options.maxFiles || 50
15895
+ });
15896
+ const result = {
15897
+ harvestedFilesCount: 0,
15898
+ totalFilesScanned: files.length,
15899
+ memoriesImported: 0,
15900
+ openLoopsDetected: 0,
15901
+ processedFiles: [],
15902
+ errors: []
15903
+ };
15904
+ for (const filePath of files) {
15905
+ try {
15906
+ const stat = statSync8(filePath);
15907
+ const prevEntry = ledger[filePath];
15908
+ if (!options.force && prevEntry && prevEntry.mtimeMs === stat.mtimeMs) {
15909
+ continue;
15910
+ }
15911
+ let content = "";
15912
+ if (stat.size > 2 * 1024 * 1024) {
15913
+ const fd = openSync(filePath, "r");
15914
+ const bufferSize = 512 * 1024;
15915
+ const buffer = Buffer.alloc(bufferSize);
15916
+ const position = Math.max(0, stat.size - bufferSize);
15917
+ const bytesRead = readSync(fd, buffer, 0, bufferSize, position);
15918
+ closeSync(fd);
15919
+ content = buffer.toString("utf8", 0, bytesRead);
15920
+ } else {
15921
+ content = readFileSync20(filePath, "utf8");
15922
+ }
15923
+ const contentHash = createHash2("sha256").update(content).digest("hex").slice(0, 16);
15924
+ if (!options.force && prevEntry && prevEntry.hash === contentHash) {
15925
+ continue;
15926
+ }
15927
+ let inferredProject = options.project;
15928
+ if (!inferredProject) {
15929
+ if (filePath.includes("/Projects/github/") || filePath.includes("/Projects/")) {
15930
+ const m = filePath.match(/\/Projects\/(?:github\/)?([^/]+)/);
15931
+ if (m)
15932
+ inferredProject = m[1];
15933
+ }
15934
+ }
15935
+ if (!inferredProject)
15936
+ inferredProject = "default";
15937
+ const importRes = importTranscript(store, content, {
15938
+ project: inferredProject,
15939
+ confirmed: options.confirmed === true,
15940
+ source: `auto_harvester:${filePath}`
15941
+ });
15942
+ ledger[filePath] = {
15943
+ mtimeMs: stat.mtimeMs,
15944
+ hash: contentHash,
15945
+ importedCount: importRes.imported,
15946
+ openLoopsCount: importRes.openLoops.length,
15947
+ harvestedAt: new Date().toISOString()
15948
+ };
15949
+ if (importRes.imported > 0 || importRes.openLoops.length > 0) {
15950
+ result.harvestedFilesCount++;
15951
+ result.memoriesImported += importRes.imported;
15952
+ result.openLoopsDetected += importRes.openLoops.length;
15953
+ result.processedFiles.push({
15954
+ path: filePath,
15955
+ imported: importRes.imported,
15956
+ openLoops: importRes.openLoops.length
15957
+ });
15958
+ recordAuditEvent(memoryDir, {
15959
+ operation: "transcript_import",
15960
+ entry_id: filePath,
15961
+ actor: "auto-harvester",
15962
+ details: {
15963
+ imported: importRes.imported,
15964
+ open_loops: importRes.openLoops.length,
15965
+ inferred_project: inferredProject
15966
+ }
15967
+ });
15968
+ }
15969
+ if (importRes.errors.length > 0) {
15970
+ result.errors.push(...importRes.errors.map((e) => `${filePath}: ${e}`));
15971
+ }
15972
+ } catch (err) {
15973
+ result.errors.push(`${filePath}: ${err.message || err}`);
15974
+ }
15975
+ }
15976
+ saveHarvestedTranscriptLedger(memoryDir, ledger);
15977
+ return result;
15978
+ }
15979
+ async function handleLearnCommand({ flags }) {
15980
+ const ctx = requireRoot(flags);
15981
+ if (!ctx)
15982
+ return 1;
15983
+ const isConfirmed = flags["confirmed"] === "true" || flags["confirm"] === "true";
15984
+ const force = flags["force"] === "true";
15985
+ const project = flags["project"];
15986
+ const maxFiles = flags["max"] ? parseInt(flags["max"], 10) : 50;
15987
+ console.log(`[Auto-Learner] Probing workstation for active AI agent transcripts...`);
15988
+ const res = harvestAllAgentTranscripts(ctx.store, {
15989
+ memoryDir: ctx.memoryDir,
15990
+ confirmed: isConfirmed,
15991
+ force,
15992
+ project,
15993
+ maxFiles
15994
+ });
15995
+ console.log(`[Auto-Learner] Scanned ${res.totalFilesScanned} agent transcript files.`);
15996
+ if (res.harvestedFilesCount === 0) {
15997
+ console.log(`✓ Up to date. 0 new un-harvested turns found (all transcripts indexed).`);
15998
+ return 0;
15999
+ }
16000
+ console.log(`
16001
+ \uD83C\uDF89 Distilled ${res.memoriesImported} new memories and ${res.openLoopsDetected} open loops from ${res.harvestedFilesCount} transcript(s):`);
16002
+ for (const p of res.processedFiles) {
16003
+ console.log(` * ${p.path} -> +${p.imported} memories, +${p.openLoops} loops`);
16004
+ }
16005
+ if (res.errors.length > 0) {
16006
+ console.log(`
16007
+ ⚠️ Warnings / Redactions during harvest:`);
16008
+ for (const err of res.errors.slice(0, 5)) {
16009
+ console.log(` - ${err}`);
16010
+ }
16011
+ }
16012
+ console.log(`
16013
+ \uD83D\uDCA1 Open 'memory ui' or run 'memory stats' to view your newly indexed cognitive graph.`);
16014
+ return 0;
16015
+ }
16016
+ var init_harvester = __esm(() => {
16017
+ init_harvest();
16018
+ init_audit();
16019
+ init_shared();
16020
+ });
16021
+
15749
16022
  // src/doctor.ts
15750
16023
  var exports_doctor = {};
15751
16024
  __export(exports_doctor, {
15752
16025
  printDoctorReport: () => printDoctorReport,
15753
16026
  runDoctor: () => runDoctor
15754
16027
  });
15755
- import { existsSync as existsSync24, statSync as statSync8, readFileSync as readFileSync20 } from "node:fs";
15756
- import { join as join21 } from "node:path";
15757
- import { homedir as homedir5 } from "node:os";
16028
+ import { existsSync as existsSync25, statSync as statSync9, readFileSync as readFileSync21 } from "node:fs";
16029
+ import { join as join22 } from "node:path";
16030
+ import { homedir as homedir6 } from "node:os";
15758
16031
  async function runDoctor(targetDir, options = {}) {
15759
- const home = homedir5();
15760
- const storagePath = options.global ? getGlobalMemoryDir() : findOrCreateProjectRoot(targetDir || process.cwd())?.memoryDir || join21(home, ".memory");
15761
- const isInitialized = existsSync24(storagePath);
16032
+ const home = homedir6();
16033
+ const storagePath = options.global ? getGlobalMemoryDir() : findOrCreateProjectRoot(targetDir || process.cwd())?.memoryDir || join22(home, ".memory");
16034
+ const isInitialized = existsSync25(storagePath);
15762
16035
  let currentMdExists = false;
15763
16036
  let currentMdLines = 0;
15764
16037
  let totalEntries = 0;
@@ -15776,10 +16049,10 @@ async function runDoctor(targetDir, options = {}) {
15776
16049
  let secretLeaksCount = 0;
15777
16050
  let brokenLinksCount = 0;
15778
16051
  if (isInitialized) {
15779
- const currentPath = join21(storagePath, "CURRENT.md");
15780
- if (existsSync24(currentPath)) {
16052
+ const currentPath = join22(storagePath, "CURRENT.md");
16053
+ if (existsSync25(currentPath)) {
15781
16054
  currentMdExists = true;
15782
- const content = readFileSync20(currentPath, "utf8");
16055
+ const content = readFileSync21(currentPath, "utf8");
15783
16056
  currentMdLines = content.split(`
15784
16057
  `).filter((l) => l.trim().length > 0).length;
15785
16058
  }
@@ -15800,21 +16073,21 @@ async function runDoctor(targetDir, options = {}) {
15800
16073
  valid = false;
15801
16074
  }
15802
16075
  }
15803
- const auditPath = join21(storagePath, "audit.jsonl");
16076
+ const auditPath = join22(storagePath, "audit.jsonl");
15804
16077
  let auditExists = false;
15805
16078
  let auditCount = 0;
15806
16079
  let auditSize = 0;
15807
16080
  let lastEvent;
15808
- if (existsSync24(auditPath)) {
16081
+ if (existsSync25(auditPath)) {
15809
16082
  auditExists = true;
15810
16083
  try {
15811
- const stat = statSync8(auditPath);
16084
+ const stat = statSync9(auditPath);
15812
16085
  auditSize = stat.size;
15813
16086
  const trail = getAuditTrail(storagePath, { limit: 1 });
15814
16087
  if (trail.length > 0) {
15815
16088
  lastEvent = `${trail[0].operation} on ${trail[0].entry_id} (${trail[0].timestamp})`;
15816
16089
  }
15817
- const raw = readFileSync20(auditPath, "utf8");
16090
+ const raw = readFileSync21(auditPath, "utf8");
15818
16091
  auditCount = raw.split(`
15819
16092
  `).filter((l) => l.trim().length > 0).length;
15820
16093
  } catch {}
@@ -15823,6 +16096,11 @@ async function runDoctor(targetDir, options = {}) {
15823
16096
  const installed = agents.filter((a) => a.installed);
15824
16097
  const connected = installed.filter((a) => a.connected);
15825
16098
  const unwired = installed.filter((a) => !a.connected);
16099
+ const { discoverAgentTranscripts: discoverAgentTranscripts2, getHarvestedTranscriptLedger: getHarvestedTranscriptLedger2 } = await Promise.resolve().then(() => (init_harvester(), exports_harvester));
16100
+ const discoveredFiles = discoverAgentTranscripts2();
16101
+ const ledger = getHarvestedTranscriptLedger2(storagePath);
16102
+ const harvestedFiles = Object.keys(ledger);
16103
+ const unharvestedCount = discoveredFiles.filter((f) => !ledger[f]).length;
15826
16104
  const memBin = findBinary("memory", home) || findBinary("musememory", home);
15827
16105
  const bunVer = typeof globalThis.Bun !== "undefined" ? globalThis.Bun.version : undefined;
15828
16106
  return {
@@ -15856,6 +16134,11 @@ async function runDoctor(targetDir, options = {}) {
15856
16134
  connectedList: connected.map((a) => a.name),
15857
16135
  unwiredList: unwired.map((a) => a.name)
15858
16136
  },
16137
+ transcripts: {
16138
+ discoveredCount: discoveredFiles.length,
16139
+ harvestedCount: harvestedFiles.length,
16140
+ unharvestedCount
16141
+ },
15859
16142
  runtime: {
15860
16143
  nodeVersion: process.version,
15861
16144
  bunVersion: bunVer,
@@ -15913,6 +16196,16 @@ function printDoctorReport(report) {
15913
16196
  console.log(` -> Run 'memory connect --all' or 'npx musememory connect --all' to wire them.`);
15914
16197
  }
15915
16198
  console.log(`
16199
+ [AUTO-LEARNER] Agent Chat Transcripts & Distillation:`);
16200
+ console.log(` Discovered Transcripts on Disk: ${report.transcripts.discoveredCount}`);
16201
+ console.log(` Harvested / Indexed: ${report.transcripts.harvestedCount}`);
16202
+ if (report.transcripts.unharvestedCount > 0) {
16203
+ console.log(` [!] New Un-Harvested Chats: ${report.transcripts.unharvestedCount}`);
16204
+ console.log(` -> Run 'memory learn' or 'memory sync-chats' to auto-distill learnings from recent AI interactions.`);
16205
+ } else {
16206
+ console.log(` [OK] All discovered chat transcripts are synchronized.`);
16207
+ }
16208
+ console.log(`
15916
16209
  [RUNTIME] Runtime & CLI Execution:`);
15917
16210
  console.log(` Node.js: ${report.runtime.nodeVersion}${report.runtime.bunVersion ? ` | Bun: v${report.runtime.bunVersion}` : ""}`);
15918
16211
  if (report.runtime.globalBinaryFound) {
@@ -15940,27 +16233,27 @@ var init_doctor = __esm(() => {
15940
16233
  });
15941
16234
 
15942
16235
  // src/retrieval/tree-index.ts
15943
- import { existsSync as existsSync25, readFileSync as readFileSync21, writeFileSync as writeFileSync12, mkdirSync as mkdirSync14, rmSync } from "node:fs";
15944
- import { join as join22 } from "node:path";
16236
+ import { existsSync as existsSync26, readFileSync as readFileSync22, writeFileSync as writeFileSync13, mkdirSync as mkdirSync15, rmSync } from "node:fs";
16237
+ import { join as join23 } from "node:path";
15945
16238
  function tokenize2(text) {
15946
16239
  if (!text || typeof text !== "string")
15947
16240
  return [];
15948
16241
  return text.toLowerCase().match(/[a-z0-9]+/g) ?? [];
15949
16242
  }
15950
16243
  function indexDir(memoryDir) {
15951
- return join22(memoryDir, "tree-index");
16244
+ return join23(memoryDir, "tree-index");
15952
16245
  }
15953
16246
  function partitionDir(idxDir, project, type2) {
15954
- return join22(idxDir, project, type2);
16247
+ return join23(idxDir, project, type2);
15955
16248
  }
15956
16249
  function shardFilePath(pDir, shardId) {
15957
- return join22(pDir, `${shardId}.json`);
16250
+ return join23(pDir, `${shardId}.json`);
15958
16251
  }
15959
16252
  function partitionIndexFilePath(pDir) {
15960
- return join22(pDir, "index.json");
16253
+ return join23(pDir, "index.json");
15961
16254
  }
15962
16255
  function globalIndexFilePath(idxDir) {
15963
- return join22(idxDir, "index.json");
16256
+ return join23(idxDir, "index.json");
15964
16257
  }
15965
16258
  function nowIso3() {
15966
16259
  return new Date().toISOString();
@@ -16050,7 +16343,7 @@ function countNodes(node) {
16050
16343
  function buildTreeIndex(store, memoryDir, config = {}) {
16051
16344
  const cfg = { ...DEFAULT_CONFIG, ...config };
16052
16345
  const allMemories = list(store).filter((e) => e.status === "confirmed");
16053
- mkdirSync14(indexDir(memoryDir), { recursive: true });
16346
+ mkdirSync15(indexDir(memoryDir), { recursive: true });
16054
16347
  if (allMemories.length === 0) {
16055
16348
  const emptyIndex = {
16056
16349
  version: 1,
@@ -16059,7 +16352,7 @@ function buildTreeIndex(store, memoryDir, config = {}) {
16059
16352
  totalMemories: 0,
16060
16353
  builtAt: nowIso3()
16061
16354
  };
16062
- writeFileSync12(globalIndexFilePath(indexDir(memoryDir)), JSON.stringify(emptyIndex, null, 2), "utf8");
16355
+ writeFileSync13(globalIndexFilePath(indexDir(memoryDir)), JSON.stringify(emptyIndex, null, 2), "utf8");
16063
16356
  return emptyIndex;
16064
16357
  }
16065
16358
  const groups = new Map;
@@ -16077,7 +16370,7 @@ function buildTreeIndex(store, memoryDir, config = {}) {
16077
16370
  for (const [key, memories] of groups) {
16078
16371
  const [project, type2] = key.split("|");
16079
16372
  const pDir = partitionDir(indexDir(memoryDir), project, type2);
16080
- mkdirSync14(pDir, { recursive: true });
16373
+ mkdirSync15(pDir, { recursive: true });
16081
16374
  const maxShardSize = cfg.shardThreshold || 1e4;
16082
16375
  const finalShards = [];
16083
16376
  const memoryChunks = [];
@@ -16138,9 +16431,9 @@ function buildTreeIndex(store, memoryDir, config = {}) {
16138
16431
  partitions[`${project}|${type2}`] = partition;
16139
16432
  totalIndexNodes += partitionNodes;
16140
16433
  totalIndexMemories += partitionMemories;
16141
- writeFileSync12(partitionIndexFilePath(pDir), JSON.stringify(partition, null, 2), "utf8");
16434
+ writeFileSync13(partitionIndexFilePath(pDir), JSON.stringify(partition, null, 2), "utf8");
16142
16435
  for (const shard of finalShards) {
16143
- writeFileSync12(shardFilePath(pDir, shard.id), JSON.stringify(shard, null, 2), "utf8");
16436
+ writeFileSync13(shardFilePath(pDir, shard.id), JSON.stringify(shard, null, 2), "utf8");
16144
16437
  }
16145
16438
  }
16146
16439
  const index = {
@@ -16150,15 +16443,15 @@ function buildTreeIndex(store, memoryDir, config = {}) {
16150
16443
  totalMemories: totalIndexMemories,
16151
16444
  builtAt: nowIso3()
16152
16445
  };
16153
- writeFileSync12(globalIndexFilePath(indexDir(memoryDir)), JSON.stringify(index, null, 2), "utf8");
16446
+ writeFileSync13(globalIndexFilePath(indexDir(memoryDir)), JSON.stringify(index, null, 2), "utf8");
16154
16447
  return index;
16155
16448
  }
16156
16449
  function loadTreeIndex(memoryDir) {
16157
16450
  const path = globalIndexFilePath(indexDir(memoryDir));
16158
- if (!existsSync25(path))
16451
+ if (!existsSync26(path))
16159
16452
  return null;
16160
16453
  try {
16161
- const data = JSON.parse(readFileSync21(path, "utf8"));
16454
+ const data = JSON.parse(readFileSync22(path, "utf8"));
16162
16455
  return data.version === 1 ? data : null;
16163
16456
  } catch {
16164
16457
  return null;
@@ -16303,10 +16596,10 @@ __export(exports_vector, {
16303
16596
  saveIndex: () => saveIndex,
16304
16597
  termFrequencies: () => termFrequencies
16305
16598
  });
16306
- import { existsSync as existsSync26, readFileSync as readFileSync22, writeFileSync as writeFileSync13, mkdirSync as mkdirSync15 } from "node:fs";
16307
- import { join as join23 } from "node:path";
16599
+ import { existsSync as existsSync27, readFileSync as readFileSync23, writeFileSync as writeFileSync14, mkdirSync as mkdirSync16 } from "node:fs";
16600
+ import { join as join24 } from "node:path";
16308
16601
  function indexFilePath(memoryDir) {
16309
- return join23(memoryDir, "index.json");
16602
+ return join24(memoryDir, "index.json");
16310
16603
  }
16311
16604
  function hashTrigram(tri) {
16312
16605
  let h = 2166136261;
@@ -16353,16 +16646,16 @@ function rebuildIndex(store) {
16353
16646
  return index;
16354
16647
  }
16355
16648
  function saveIndex(index, memoryDir) {
16356
- if (!existsSync26(memoryDir))
16357
- mkdirSync15(memoryDir, { recursive: true });
16358
- writeFileSync13(indexFilePath(memoryDir), JSON.stringify(index), "utf8");
16649
+ if (!existsSync27(memoryDir))
16650
+ mkdirSync16(memoryDir, { recursive: true });
16651
+ writeFileSync14(indexFilePath(memoryDir), JSON.stringify(index), "utf8");
16359
16652
  }
16360
16653
  function loadIndex(memoryDir) {
16361
16654
  const p = indexFilePath(memoryDir);
16362
- if (!existsSync26(p))
16655
+ if (!existsSync27(p))
16363
16656
  return null;
16364
16657
  try {
16365
- const parsed = JSON.parse(readFileSync22(p, "utf8"));
16658
+ const parsed = JSON.parse(readFileSync23(p, "utf8"));
16366
16659
  return parsed.version === 1 ? parsed : null;
16367
16660
  } catch {
16368
16661
  return null;
@@ -16434,8 +16727,8 @@ var init_vector = __esm(() => {
16434
16727
  });
16435
16728
 
16436
16729
  // src/pageindex/engine.ts
16437
- import { existsSync as existsSync27, mkdirSync as mkdirSync16, readFileSync as readFileSync23, writeFileSync as writeFileSync14, rmSync as rmSync2, readdirSync as readdirSync9 } from "node:fs";
16438
- import { join as join24 } from "node:path";
16730
+ import { existsSync as existsSync28, mkdirSync as mkdirSync17, readFileSync as readFileSync24, writeFileSync as writeFileSync15, rmSync as rmSync2, readdirSync as readdirSync10 } from "node:fs";
16731
+ import { join as join25 } from "node:path";
16439
16732
  function slugify2(text) {
16440
16733
  return text.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 50);
16441
16734
  }
@@ -16512,9 +16805,9 @@ function buildPageIndex(content, options) {
16512
16805
  root: rootNode
16513
16806
  };
16514
16807
  if (options.memoryDir && !options.dryRun) {
16515
- const dir = join24(options.memoryDir, "pageindex", project);
16516
- mkdirSync16(dir, { recursive: true });
16517
- writeFileSync14(join24(dir, `${indexId}.json`), JSON.stringify(doc, null, 2), "utf8");
16808
+ const dir = join25(options.memoryDir, "pageindex", project);
16809
+ mkdirSync17(dir, { recursive: true });
16810
+ writeFileSync15(join25(dir, `${indexId}.json`), JSON.stringify(doc, null, 2), "utf8");
16518
16811
  }
16519
16812
  return doc;
16520
16813
  }
@@ -16578,29 +16871,29 @@ function searchPageIndex(doc, options) {
16578
16871
  };
16579
16872
  }
16580
16873
  function loadPageIndex(memoryDir, project, indexId) {
16581
- const filePath = join24(memoryDir, "pageindex", project, `${indexId}.json`);
16582
- if (!existsSync27(filePath))
16874
+ const filePath = join25(memoryDir, "pageindex", project, `${indexId}.json`);
16875
+ if (!existsSync28(filePath))
16583
16876
  return null;
16584
16877
  try {
16585
- return JSON.parse(readFileSync23(filePath, "utf8"));
16878
+ return JSON.parse(readFileSync24(filePath, "utf8"));
16586
16879
  } catch {
16587
16880
  return null;
16588
16881
  }
16589
16882
  }
16590
16883
  function listPageIndexes(memoryDir, project) {
16591
- const base = join24(memoryDir, "pageindex");
16592
- if (!existsSync27(base))
16884
+ const base = join25(memoryDir, "pageindex");
16885
+ if (!existsSync28(base))
16593
16886
  return [];
16594
16887
  const results = [];
16595
- const projects = project ? [project] : readdirSync9(base);
16888
+ const projects = project ? [project] : readdirSync10(base);
16596
16889
  for (const p of projects) {
16597
- const pDir = join24(base, p);
16598
- if (!existsSync27(pDir))
16890
+ const pDir = join25(base, p);
16891
+ if (!existsSync28(pDir))
16599
16892
  continue;
16600
- for (const f of readdirSync9(pDir)) {
16893
+ for (const f of readdirSync10(pDir)) {
16601
16894
  if (f.endsWith(".json")) {
16602
16895
  try {
16603
- results.push(JSON.parse(readFileSync23(join24(pDir, f), "utf8")));
16896
+ results.push(JSON.parse(readFileSync24(join25(pDir, f), "utf8")));
16604
16897
  } catch {}
16605
16898
  }
16606
16899
  }
@@ -16608,8 +16901,8 @@ function listPageIndexes(memoryDir, project) {
16608
16901
  return results;
16609
16902
  }
16610
16903
  function deletePageIndex(memoryDir, project, indexId) {
16611
- const base = join24(memoryDir, "pageindex");
16612
- if (!existsSync27(base))
16904
+ const base = join25(memoryDir, "pageindex");
16905
+ if (!existsSync28(base))
16613
16906
  return { deletedCount: 0 };
16614
16907
  let count = 0;
16615
16908
  if (!project && !indexId) {
@@ -16617,14 +16910,14 @@ function deletePageIndex(memoryDir, project, indexId) {
16617
16910
  return { deletedCount: 1 };
16618
16911
  }
16619
16912
  if (project && !indexId) {
16620
- const pDir = join24(base, project);
16621
- if (existsSync27(pDir)) {
16913
+ const pDir = join25(base, project);
16914
+ if (existsSync28(pDir)) {
16622
16915
  rmSync2(pDir, { recursive: true, force: true });
16623
16916
  count++;
16624
16917
  }
16625
16918
  } else if (project && indexId) {
16626
- const file = join24(base, project, `${indexId}.json`);
16627
- if (existsSync27(file)) {
16919
+ const file = join25(base, project, `${indexId}.json`);
16920
+ if (existsSync28(file)) {
16628
16921
  rmSync2(file, { force: true });
16629
16922
  count++;
16630
16923
  }
@@ -16637,13 +16930,13 @@ var init_engine2 = __esm(() => {
16637
16930
  });
16638
16931
 
16639
16932
  // src/retrieval/engine.ts
16640
- import { existsSync as existsSync28 } from "node:fs";
16641
- import { join as join25 } from "node:path";
16933
+ import { existsSync as existsSync29 } from "node:fs";
16934
+ import { join as join26 } from "node:path";
16642
16935
 
16643
16936
  class RetrievalEngine {
16644
16937
  static search(store, memoryDir, query, options = {}) {
16645
16938
  const mode = options.mode ?? "auto";
16646
- if (mode === "pageindex" || options.docPath && existsSync28(options.docPath)) {
16939
+ if (mode === "pageindex" || options.docPath && existsSync29(options.docPath)) {
16647
16940
  try {
16648
16941
  const docs = listPageIndexes(memoryDir, options.project);
16649
16942
  if (docs.length > 0) {
@@ -16670,8 +16963,8 @@ class RetrievalEngine {
16670
16963
  }
16671
16964
  } catch {}
16672
16965
  }
16673
- const treeDir = join25(memoryDir, "tree");
16674
- const hasTree = existsSync28(treeDir);
16966
+ const treeDir = join26(memoryDir, "tree");
16967
+ const hasTree = existsSync29(treeDir);
16675
16968
  const allEntries = list(store);
16676
16969
  if (mode === "tree" || mode === "auto" && hasTree && allEntries.length >= 500) {
16677
16970
  try {
@@ -16709,7 +17002,7 @@ class RetrievalEngine {
16709
17002
  } catch {}
16710
17003
  }
16711
17004
  const vecPath = indexFilePath(memoryDir);
16712
- const hasVec = existsSync28(vecPath);
17005
+ const hasVec = existsSync29(vecPath);
16713
17006
  if (mode === "hybrid" || mode === "auto" && hasVec && allEntries.length > 100) {
16714
17007
  try {
16715
17008
  const hybridResults = hybridSearch(store, memoryDir, query, {
@@ -26405,8 +26698,8 @@ var init_loops = __esm(() => {
26405
26698
  });
26406
26699
 
26407
26700
  // src/distill.ts
26408
- import { existsSync as existsSync29, mkdirSync as mkdirSync17, writeFileSync as writeFileSync15 } from "node:fs";
26409
- import { join as join26 } from "node:path";
26701
+ import { existsSync as existsSync30, mkdirSync as mkdirSync18, writeFileSync as writeFileSync16 } from "node:fs";
26702
+ import { join as join27 } from "node:path";
26410
26703
  function fixTokens(e) {
26411
26704
  const bag = tokenBag(e.title, 2);
26412
26705
  mergeBag(bag, tokenBag((e.tags ?? []).join(" "), 2));
@@ -26417,7 +26710,7 @@ function distillSkills(store, workspaceRoot, options = {}) {
26417
26710
  const report = { created: [], skippedExisting: [], clustersBelowThreshold: 0 };
26418
26711
  const fixes = list(store).filter((e) => e.status === "confirmed" && e.type === "fix").sort((a, b) => Date.parse(a.created_at) - Date.parse(b.created_at));
26419
26712
  const clusters = clusterByTokenOverlap(fixes, fixTokens, 0.5);
26420
- const skillsDir = options.skillsDir ?? join26(workspaceRoot, ".agents", "skills");
26713
+ const skillsDir = options.skillsDir ?? join27(workspaceRoot, ".agents", "skills");
26421
26714
  for (const cluster of clusters) {
26422
26715
  if (cluster.length < minCount) {
26423
26716
  report.clustersBelowThreshold++;
@@ -26425,9 +26718,9 @@ function distillSkills(store, workspaceRoot, options = {}) {
26425
26718
  }
26426
26719
  const topic = dominantTopicTokens(cluster.map((e) => e.title));
26427
26720
  const slug = slugifyId(topic.join("-") || "skill");
26428
- const dir = join26(skillsDir, slug);
26721
+ const dir = join27(skillsDir, slug);
26429
26722
  const memberIds = cluster.map((e) => e.id);
26430
- if (existsSync29(dir)) {
26723
+ if (existsSync30(dir)) {
26431
26724
  report.skippedExisting.push({ slug, members: memberIds });
26432
26725
  continue;
26433
26726
  }
@@ -26437,10 +26730,10 @@ function distillSkills(store, workspaceRoot, options = {}) {
26437
26730
  throw new Error(`Secret detected in generated SKILL.md for '${slug}': ${secrets.join(", ")}`);
26438
26731
  }
26439
26732
  if (!options.dryRun) {
26440
- mkdirSync17(dir, { recursive: true });
26441
- writeFileSync15(join26(dir, "SKILL.md"), md, "utf8");
26733
+ mkdirSync18(dir, { recursive: true });
26734
+ writeFileSync16(join27(dir, "SKILL.md"), md, "utf8");
26442
26735
  }
26443
- report.created.push({ slug, path: join26(dir, "SKILL.md"), members: memberIds });
26736
+ report.created.push({ slug, path: join27(dir, "SKILL.md"), members: memberIds });
26444
26737
  }
26445
26738
  return report;
26446
26739
  }
@@ -26778,14 +27071,14 @@ var init_schema2 = __esm(() => {
26778
27071
  });
26779
27072
 
26780
27073
  // src/settings/loader.ts
26781
- import { existsSync as existsSync30, mkdirSync as mkdirSync18, readFileSync as readFileSync24, writeFileSync as writeFileSync16 } from "node:fs";
26782
- import { join as join27 } from "node:path";
27074
+ import { existsSync as existsSync31, mkdirSync as mkdirSync19, readFileSync as readFileSync25, writeFileSync as writeFileSync17 } from "node:fs";
27075
+ import { join as join28 } from "node:path";
26783
27076
  function getSettingsFilePath(memoryDir) {
26784
- return join27(memoryDir, "settings.json");
27077
+ return join28(memoryDir, "settings.json");
26785
27078
  }
26786
27079
  function loadSettings(memoryDir) {
26787
27080
  const filePath = getSettingsFilePath(memoryDir);
26788
- if (!existsSync30(filePath)) {
27081
+ if (!existsSync31(filePath)) {
26789
27082
  return {
26790
27083
  version: 1,
26791
27084
  global: JSON.parse(JSON.stringify(DEFAULT_SETTINGS)),
@@ -26793,7 +27086,7 @@ function loadSettings(memoryDir) {
26793
27086
  };
26794
27087
  }
26795
27088
  try {
26796
- const raw = readFileSync24(filePath, "utf8");
27089
+ const raw = readFileSync25(filePath, "utf8");
26797
27090
  const parsed = JSON.parse(raw);
26798
27091
  return {
26799
27092
  version: parsed.version || 1,
@@ -26809,9 +27102,9 @@ function loadSettings(memoryDir) {
26809
27102
  }
26810
27103
  }
26811
27104
  function saveSettings(store, memoryDir) {
26812
- mkdirSync18(memoryDir, { recursive: true });
27105
+ mkdirSync19(memoryDir, { recursive: true });
26813
27106
  const filePath = getSettingsFilePath(memoryDir);
26814
- writeFileSync16(filePath, JSON.stringify(store, null, 2), "utf8");
27107
+ writeFileSync17(filePath, JSON.stringify(store, null, 2), "utf8");
26815
27108
  }
26816
27109
  function getSettings(memoryDir) {
26817
27110
  return loadSettings(memoryDir).global;
@@ -26927,17 +27220,17 @@ __export(exports_plasticity, {
26927
27220
  recordCoActivation: () => recordCoActivation,
26928
27221
  saveHebbianGraph: () => saveHebbianGraph
26929
27222
  });
26930
- import { existsSync as existsSync31, readFileSync as readFileSync25, writeFileSync as writeFileSync17 } from "node:fs";
26931
- import { join as join28 } from "node:path";
27223
+ import { existsSync as existsSync32, readFileSync as readFileSync26, writeFileSync as writeFileSync18 } from "node:fs";
27224
+ import { join as join29 } from "node:path";
26932
27225
  function getPlasticityPath(memoryDir) {
26933
- return join28(memoryDir, "hebbian-plasticity.json");
27226
+ return join29(memoryDir, "hebbian-plasticity.json");
26934
27227
  }
26935
27228
  function loadHebbianGraph(memoryDir) {
26936
27229
  const p = getPlasticityPath(memoryDir);
26937
- if (!existsSync31(p))
27230
+ if (!existsSync32(p))
26938
27231
  return {};
26939
27232
  try {
26940
- return JSON.parse(readFileSync25(p, "utf8"));
27233
+ return JSON.parse(readFileSync26(p, "utf8"));
26941
27234
  } catch {
26942
27235
  return {};
26943
27236
  }
@@ -26945,7 +27238,7 @@ function loadHebbianGraph(memoryDir) {
26945
27238
  function saveHebbianGraph(memoryDir, graph) {
26946
27239
  const p = getPlasticityPath(memoryDir);
26947
27240
  try {
26948
- writeFileSync17(p, JSON.stringify(graph, null, 2), "utf8");
27241
+ writeFileSync18(p, JSON.stringify(graph, null, 2), "utf8");
26949
27242
  } catch {}
26950
27243
  }
26951
27244
  function recordCoActivation(memoryIds, memoryDir) {
@@ -27036,8 +27329,8 @@ __export(exports_drift, {
27036
27329
  scanCodeDriftFromDiff: () => scanCodeDriftFromDiff
27037
27330
  });
27038
27331
  import { execSync as execSync3 } from "node:child_process";
27039
- import { existsSync as existsSync32 } from "node:fs";
27040
- import { join as join29, isAbsolute as isAbsolute2 } from "node:path";
27332
+ import { existsSync as existsSync33 } from "node:fs";
27333
+ import { join as join30, isAbsolute as isAbsolute2 } from "node:path";
27041
27334
  function scanCodeDriftFromDiff(entries, changedFiles, workspaceRoot) {
27042
27335
  const root = workspaceRoot || process.cwd();
27043
27336
  const normalizedChanged = new Set(changedFiles.map((f) => f.trim().replace(/^[\.\/]+/, "")).filter(Boolean));
@@ -27051,8 +27344,8 @@ function scanCodeDriftFromDiff(entries, changedFiles, workspaceRoot) {
27051
27344
  const norm = p.trim().replace(/^[\.\/]+/, "");
27052
27345
  const matched = Array.from(normalizedChanged).find((cf) => cf === norm || cf.endsWith(`/${norm}`) || norm.endsWith(`/${cf}`));
27053
27346
  if (matched) {
27054
- const fullPath = isAbsolute2(p) ? p : join29(root, p);
27055
- const exists = existsSync32(fullPath);
27347
+ const fullPath = isAbsolute2(p) ? p : join30(root, p);
27348
+ const exists = existsSync33(fullPath);
27056
27349
  drifted.push({
27057
27350
  memoryId: entry.id,
27058
27351
  title: entry.title,
@@ -27798,6 +28091,12 @@ You are equipped with Muse Memory, an autonomous persistent cognitive memory sys
27798
28091
  recordCoActivation2(formatted.entries.map((e) => e.entry.id), activeMemoryDir);
27799
28092
  } catch {}
27800
28093
  }
28094
+ if (activeMemoryDir) {
28095
+ try {
28096
+ const { harvestAllAgentTranscripts: harvestAllAgentTranscripts2 } = await Promise.resolve().then(() => (init_harvester(), exports_harvester));
28097
+ harvestAllAgentTranscripts2(activeStore, { memoryDir: activeMemoryDir, maxFiles: 3 });
28098
+ } catch {}
28099
+ }
27801
28100
  let finalMarkdown = formatted.markdown;
27802
28101
  if (a.compress === true) {
27803
28102
  const { compressPromptContext: compressPromptContext2 } = await Promise.resolve().then(() => exports_compress);
@@ -27890,9 +28189,18 @@ You are equipped with Muse Memory, an autonomous persistent cognitive memory sys
27890
28189
  }
27891
28190
  }
27892
28191
  case "memory_harvest": {
28192
+ if (a.all === true || a.auto === true || !a.text) {
28193
+ const { harvestAllAgentTranscripts: harvestAllAgentTranscripts2 } = await Promise.resolve().then(() => (init_harvester(), exports_harvester));
28194
+ const harvestRes = harvestAllAgentTranscripts2(activeStore, {
28195
+ memoryDir: activeMemoryDir,
28196
+ confirmed: a.confirmed === true,
28197
+ project: a.project ? String(a.project) : undefined
28198
+ });
28199
+ return toolResult(harvestRes);
28200
+ }
27893
28201
  const created = harvestMemories(activeStore, {
27894
28202
  text: String(a.text),
27895
- project: String(a.project),
28203
+ project: String(a.project || "default"),
27896
28204
  confirmed: a.confirmed === true
27897
28205
  });
27898
28206
  return toolResult({ harvested_count: created.length, entries: created });
@@ -28314,8 +28622,8 @@ __export(exports_daemon, {
28314
28622
  startHub: () => startHub
28315
28623
  });
28316
28624
  import { createServer as createServer2 } from "node:http";
28317
- import { existsSync as existsSync33 } from "node:fs";
28318
- import { join as join30 } from "node:path";
28625
+ import { existsSync as existsSync34 } from "node:fs";
28626
+ import { join as join31 } from "node:path";
28319
28627
  import { watch } from "node:fs";
28320
28628
  function startHub(port, memoryDir) {
28321
28629
  const clients = new Set;
@@ -28381,7 +28689,7 @@ data: ${JSON.stringify(event)}
28381
28689
  return clients.size;
28382
28690
  }
28383
28691
  if (memoryDir) {
28384
- const memoriesDir = join30(memoryDir, "memories");
28692
+ const memoriesDir = join31(memoryDir, "memories");
28385
28693
  const armDebounced = (type2) => {
28386
28694
  if (debounceTimer)
28387
28695
  clearTimeout(debounceTimer);
@@ -28390,11 +28698,11 @@ data: ${JSON.stringify(event)}
28390
28698
  }, DEBOUNCE_MS);
28391
28699
  };
28392
28700
  try {
28393
- if (existsSync33(memoriesDir)) {
28701
+ if (existsSync34(memoriesDir)) {
28394
28702
  watchers.push(watch(memoriesDir, () => armDebounced("memory.changed")));
28395
28703
  }
28396
- const currentMd = join30(memoryDir, "CURRENT.md");
28397
- if (existsSync33(currentMd)) {
28704
+ const currentMd = join31(memoryDir, "CURRENT.md");
28705
+ if (existsSync34(currentMd)) {
28398
28706
  watchers.push(watch(currentMd, () => armDebounced("constraints.changed")));
28399
28707
  }
28400
28708
  } catch {}
@@ -28427,19 +28735,19 @@ var DEBOUNCE_MS = 300;
28427
28735
  var init_daemon = () => {};
28428
28736
 
28429
28737
  // src/cli/ecosystem.ts
28430
- import { join as join31 } from "node:path";
28431
- import { existsSync as existsSync34, mkdirSync as mkdirSync19, writeFileSync as writeFileSync18, readFileSync as readFileSync26 } from "node:fs";
28432
- import { homedir as homedir6 } from "node:os";
28738
+ import { join as join32 } from "node:path";
28739
+ import { existsSync as existsSync35, mkdirSync as mkdirSync20, writeFileSync as writeFileSync19, readFileSync as readFileSync27 } from "node:fs";
28740
+ import { homedir as homedir7 } from "node:os";
28433
28741
  function ensureProjectAgentInstructions(targetDir) {
28434
- const agentsMdPath = join31(targetDir, "AGENTS.md");
28435
- if (existsSync34(agentsMdPath)) {
28742
+ const agentsMdPath = join32(targetDir, "AGENTS.md");
28743
+ if (existsSync35(agentsMdPath)) {
28436
28744
  try {
28437
- const existing = readFileSync26(agentsMdPath, "utf8");
28745
+ const existing = readFileSync27(agentsMdPath, "utf8");
28438
28746
  if (existing.includes("<!-- agentmemory:start -->")) {
28439
28747
  const updated = existing.replace(/<!-- agentmemory:start -->[\s\S]*?<!-- agentmemory:end -->/, MUSE_MEMORY_DIRECTIVE);
28440
- writeFileSync18(agentsMdPath, updated, "utf8");
28748
+ writeFileSync19(agentsMdPath, updated, "utf8");
28441
28749
  } else if (!existing.includes("<!-- musememory:start -->") && !existing.includes("Muse Memory")) {
28442
- writeFileSync18(agentsMdPath, `${existing.trim()}
28750
+ writeFileSync19(agentsMdPath, `${existing.trim()}
28443
28751
 
28444
28752
  ${MUSE_MEMORY_DIRECTIVE}
28445
28753
  `, "utf8");
@@ -28450,23 +28758,23 @@ ${MUSE_MEMORY_DIRECTIVE}
28450
28758
 
28451
28759
  ${MUSE_MEMORY_DIRECTIVE}
28452
28760
  `;
28453
- writeFileSync18(agentsMdPath, initialContent, "utf8");
28761
+ writeFileSync19(agentsMdPath, initialContent, "utf8");
28454
28762
  }
28455
28763
  }
28456
28764
  async function handleInstallCommand({ positional, flags }) {
28457
28765
  const isGlobal = flags["global"] === "true" || flags["g"] === "true";
28458
- const targetDir = positional[0] ? join31(process.cwd(), positional[0]) : process.cwd();
28459
- const memoryDir = isGlobal ? getGlobalMemoryDir() : join31(targetDir, ".memory");
28766
+ const targetDir = positional[0] ? join32(process.cwd(), positional[0]) : process.cwd();
28767
+ const memoryDir = isGlobal ? getGlobalMemoryDir() : join32(targetDir, ".memory");
28460
28768
  const globalDir = getGlobalMemoryDir();
28461
- mkdirSync19(join31(memoryDir, "memories"), { recursive: true });
28462
- mkdirSync19(join31(globalDir, "memories"), { recursive: true });
28463
- const currentPath = join31(memoryDir, "CURRENT.md");
28464
- if (!existsSync34(currentPath)) {
28465
- writeFileSync18(currentPath, `# Active Project Constraints
28769
+ mkdirSync20(join32(memoryDir, "memories"), { recursive: true });
28770
+ mkdirSync20(join32(globalDir, "memories"), { recursive: true });
28771
+ const currentPath = join32(memoryDir, "CURRENT.md");
28772
+ if (!existsSync35(currentPath)) {
28773
+ writeFileSync19(currentPath, `# Active Project Constraints
28466
28774
  `, "utf8");
28467
28775
  }
28468
28776
  const globalUserPath = userFilePath(globalDir);
28469
- if (!existsSync34(globalUserPath)) {
28777
+ if (!existsSync35(globalUserPath)) {
28470
28778
  const { promptSingleSelect: promptSingleSelect2 } = await Promise.resolve().then(() => (init_prompt(), exports_prompt));
28471
28779
  const archetypeOptions = [
28472
28780
  { id: "developer", label: "Software Engineer / Developer", hint: "Code-first, direct, strict typing" },
@@ -28482,13 +28790,13 @@ async function handleInstallCommand({ positional, flags }) {
28482
28790
  if (!isGlobal) {
28483
28791
  ensureProjectAgentInstructions(targetDir);
28484
28792
  }
28485
- const globalAgentsMd = join31(homedir6(), ".agents", "AGENTS.md");
28486
- if (existsSync34(globalAgentsMd)) {
28793
+ const globalAgentsMd = join32(homedir7(), ".agents", "AGENTS.md");
28794
+ if (existsSync35(globalAgentsMd)) {
28487
28795
  try {
28488
- const existing = readFileSync26(globalAgentsMd, "utf8");
28796
+ const existing = readFileSync27(globalAgentsMd, "utf8");
28489
28797
  if (existing.includes("<!-- agentmemory:start -->")) {
28490
28798
  const updated = existing.replace(/<!-- agentmemory:start -->[\s\S]*?<!-- agentmemory:end -->/, MUSE_MEMORY_DIRECTIVE);
28491
- writeFileSync18(globalAgentsMd, updated, "utf8");
28799
+ writeFileSync19(globalAgentsMd, updated, "utf8");
28492
28800
  }
28493
28801
  } catch {}
28494
28802
  }
@@ -28549,11 +28857,11 @@ async function handleInstallCommand({ positional, flags }) {
28549
28857
  return 0;
28550
28858
  }
28551
28859
  async function promptOrNotifyGitHook(targetDir, autoInstall = false) {
28552
- const gitDir = join31(targetDir, ".git");
28553
- if (!existsSync34(gitDir))
28860
+ const gitDir = join32(targetDir, ".git");
28861
+ if (!existsSync35(gitDir))
28554
28862
  return false;
28555
- const hookPath = join31(gitDir, "hooks", "pre-commit");
28556
- const isInstalled = existsSync34(hookPath) && readFileSync26(hookPath, "utf8").includes("memory harvest-auto");
28863
+ const hookPath = join32(gitDir, "hooks", "pre-commit");
28864
+ const isInstalled = existsSync35(hookPath) && readFileSync27(hookPath, "utf8").includes("memory harvest-auto");
28557
28865
  if (isInstalled)
28558
28866
  return true;
28559
28867
  if (autoInstall) {
@@ -28609,7 +28917,7 @@ async function handleUninstallCommand({ positional, flags }) {
28609
28917
  action: async () => {
28610
28918
  if (purge) {
28611
28919
  const ctx = requireRoot(flags);
28612
- if (ctx && existsSync34(ctx.memoryDir) && !dryRun) {
28920
+ if (ctx && existsSync35(ctx.memoryDir) && !dryRun) {
28613
28921
  const { rmSync: rmSync3 } = await import("node:fs");
28614
28922
  rmSync3(ctx.memoryDir, { recursive: true, force: true });
28615
28923
  }
@@ -28641,13 +28949,13 @@ async function handleUninstallCommand({ positional, flags }) {
28641
28949
  }
28642
28950
  async function handleInitCommand({ positional, flags }) {
28643
28951
  const isGlobal = flags["global"] === "true";
28644
- const targetDir = positional[0] ? join31(process.cwd(), positional[0]) : process.cwd();
28952
+ const targetDir = positional[0] ? join32(process.cwd(), positional[0]) : process.cwd();
28645
28953
  const memoryDirName = flags["legacy"] === "true" ? ".musememory" : ".memory";
28646
- const memoryDir = isGlobal ? getGlobalMemoryDir() : join31(targetDir, memoryDirName);
28647
- mkdirSync19(join31(memoryDir, "memories"), { recursive: true });
28648
- const currentPath = join31(memoryDir, "CURRENT.md");
28649
- if (!existsSync34(currentPath)) {
28650
- writeFileSync18(currentPath, `# Active Project Constraints
28954
+ const memoryDir = isGlobal ? getGlobalMemoryDir() : join32(targetDir, memoryDirName);
28955
+ mkdirSync20(join32(memoryDir, "memories"), { recursive: true });
28956
+ const currentPath = join32(memoryDir, "CURRENT.md");
28957
+ if (!existsSync35(currentPath)) {
28958
+ writeFileSync19(currentPath, `# Active Project Constraints
28651
28959
  `, "utf8");
28652
28960
  }
28653
28961
  if (!isGlobal) {
@@ -28823,6 +29131,13 @@ async function handleUiCommand({ flags }) {
28823
29131
  if (!ctx)
28824
29132
  return 1;
28825
29133
  const port = flags["port"] ? parseInt(flags["port"], 10) : 2222;
29134
+ try {
29135
+ const { harvestAllAgentTranscripts: harvestAllAgentTranscripts2 } = await Promise.resolve().then(() => (init_harvester(), exports_harvester));
29136
+ const harvestRes = harvestAllAgentTranscripts2(ctx.store, { memoryDir: ctx.memoryDir });
29137
+ if (harvestRes.harvestedFilesCount > 0) {
29138
+ console.log(`[UI Auto-Sync] Distilled ${harvestRes.memoriesImported} new memories from ${harvestRes.harvestedFilesCount} agent transcript(s).`);
29139
+ }
29140
+ } catch {}
28826
29141
  const { startUiServer: startUiServer2 } = await Promise.resolve().then(() => (init_ui(), exports_ui));
28827
29142
  const srv = await startUiServer2({
28828
29143
  port,
@@ -28931,8 +29246,8 @@ var init_ecosystem = __esm(() => {
28931
29246
  });
28932
29247
 
28933
29248
  // src/agentcontract.ts
28934
- import { existsSync as existsSync35 } from "node:fs";
28935
- import { join as join32 } from "node:path";
29249
+ import { existsSync as existsSync36 } from "node:fs";
29250
+ import { join as join33 } from "node:path";
28936
29251
  function parseAgentMemoryContract(markdown) {
28937
29252
  if (!markdown || !markdown.startsWith("---"))
28938
29253
  return null;
@@ -28964,17 +29279,17 @@ function parseAgentMemoryContract(markdown) {
28964
29279
  function resolveAgentFile(nameOrPath, workspaceRoot) {
28965
29280
  if (!nameOrPath)
28966
29281
  return null;
28967
- if (existsSync35(nameOrPath))
29282
+ if (existsSync36(nameOrPath))
28968
29283
  return nameOrPath;
28969
29284
  const candidates = [
28970
- join32(workspaceRoot, ".agents", `${nameOrPath}.md`),
28971
- join32(workspaceRoot, ".memory", "agents", `${nameOrPath}.md`)
29285
+ join33(workspaceRoot, ".agents", `${nameOrPath}.md`),
29286
+ join33(workspaceRoot, ".memory", "agents", `${nameOrPath}.md`)
28972
29287
  ];
28973
29288
  const home = process.env.HOME;
28974
29289
  if (home)
28975
- candidates.push(join32(home, ".config", "opencode", "agents", `${nameOrPath}.md`));
29290
+ candidates.push(join33(home, ".config", "opencode", "agents", `${nameOrPath}.md`));
28976
29291
  for (const c of candidates) {
28977
- if (existsSync35(c))
29292
+ if (existsSync36(c))
28978
29293
  return c;
28979
29294
  }
28980
29295
  return null;
@@ -28984,8 +29299,8 @@ var init_agentcontract = __esm(() => {
28984
29299
  });
28985
29300
 
28986
29301
  // src/cli/retrieval.ts
28987
- import { existsSync as existsSync36, readFileSync as readFileSync27 } from "node:fs";
28988
- import { join as join33 } from "node:path";
29302
+ import { existsSync as existsSync37, readFileSync as readFileSync28 } from "node:fs";
29303
+ import { join as join34 } from "node:path";
28989
29304
  async function handleContextCommand({ positional, flags }) {
28990
29305
  let agentTypes;
28991
29306
  let agentTags;
@@ -28993,7 +29308,7 @@ async function handleContextCommand({ positional, flags }) {
28993
29308
  const file = resolveAgentFile(flags["for-agent"], flags["dir"] ?? process.cwd());
28994
29309
  if (!file)
28995
29310
  return usageError(`--for-agent: cannot resolve agent '${flags["for-agent"]}'`);
28996
- const contract = parseAgentMemoryContract(readFileSync27(file, "utf8"));
29311
+ const contract = parseAgentMemoryContract(readFileSync28(file, "utf8"));
28997
29312
  if (contract) {
28998
29313
  agentTypes = contract.types;
28999
29314
  agentTags = contract.tags;
@@ -29133,7 +29448,7 @@ async function handleHarvestCommand({ positional, flags }) {
29133
29448
  const project = flags["project"];
29134
29449
  if (!project)
29135
29450
  return usageError("harvest requires --project");
29136
- const rawText = existsSync36(target) ? readFileSync27(target, "utf8") : target;
29451
+ const rawText = existsSync37(target) ? readFileSync28(target, "utf8") : target;
29137
29452
  const created = harvestMemories(ctx.store, {
29138
29453
  text: rawText,
29139
29454
  project,
@@ -29169,8 +29484,8 @@ async function handleSearchTranscriptCommand({ positional, flags }) {
29169
29484
  const filePath = positional[1];
29170
29485
  if (!query)
29171
29486
  return usageError("search-transcript requires <query> [file.jsonl]");
29172
- const targetFile = filePath ? filePath.startsWith("/") ? filePath : join33(process.cwd(), filePath) : join33(process.cwd(), "transcript.jsonl");
29173
- if (!existsSync36(targetFile)) {
29487
+ const targetFile = filePath ? filePath.startsWith("/") ? filePath : join34(process.cwd(), filePath) : join34(process.cwd(), "transcript.jsonl");
29488
+ if (!existsSync37(targetFile)) {
29174
29489
  return fail(`error: transcript file not found: ${targetFile}`);
29175
29490
  }
29176
29491
  const windowSize = flags["window"] ? parseInt(flags["window"], 10) : 2;
@@ -29186,7 +29501,7 @@ async function handleReindexCommand({ flags }) {
29186
29501
  const { rebuildIndex: rebuildIndex2, saveIndex: saveIndex2 } = await Promise.resolve().then(() => (init_vector(), exports_vector));
29187
29502
  const index = rebuildIndex2(ctx.store);
29188
29503
  saveIndex2(index, ctx.memoryDir);
29189
- console.log(`indexed ${Object.keys(index.entries).length} entries -> ${join33(ctx.memoryDir, "index.json")}`);
29504
+ console.log(`indexed ${Object.keys(index.entries).length} entries -> ${join34(ctx.memoryDir, "index.json")}`);
29190
29505
  return 0;
29191
29506
  }
29192
29507
  async function handleHookCommand({ positional, flags }) {
@@ -29577,7 +29892,7 @@ var init_entities2 = __esm(() => {
29577
29892
  });
29578
29893
 
29579
29894
  // src/cli/settings.ts
29580
- import { writeFileSync as writeFileSync19, readFileSync as readFileSync28 } from "node:fs";
29895
+ import { writeFileSync as writeFileSync20, readFileSync as readFileSync29 } from "node:fs";
29581
29896
  function handleSettingsCommand(parsed) {
29582
29897
  const ctx = requireRoot(parsed.flags);
29583
29898
  if (!ctx)
@@ -29659,7 +29974,7 @@ function handleSettingsCommand(parsed) {
29659
29974
  const out = parsed.flags.out;
29660
29975
  const json2 = exportSettings(memoryDir);
29661
29976
  if (out) {
29662
- writeFileSync19(out, json2, "utf8");
29977
+ writeFileSync20(out, json2, "utf8");
29663
29978
  console.log(`Exported settings to ${out}`);
29664
29979
  } else {
29665
29980
  console.log(json2);
@@ -29673,7 +29988,7 @@ function handleSettingsCommand(parsed) {
29673
29988
  return 2;
29674
29989
  }
29675
29990
  try {
29676
- const raw = readFileSync28(file, "utf8");
29991
+ const raw = readFileSync29(file, "utf8");
29677
29992
  importSettings(memoryDir, raw);
29678
29993
  console.log(`Successfully imported settings from ${file}`);
29679
29994
  return 0;
@@ -29696,16 +30011,16 @@ var init_nudge = __esm(() => {
29696
30011
  });
29697
30012
 
29698
30013
  // src/routines.ts
29699
- import { readFileSync as readFileSync29, existsSync as existsSync37 } from "node:fs";
29700
- import { join as join34 } from "node:path";
30014
+ import { readFileSync as readFileSync30, existsSync as existsSync38 } from "node:fs";
30015
+ import { join as join35 } from "node:path";
29701
30016
  function routinesPath(memoryDir) {
29702
- return join34(memoryDir, "routines.yaml");
30017
+ return join35(memoryDir, "routines.yaml");
29703
30018
  }
29704
30019
  function loadRoutines(memoryDir) {
29705
30020
  const p = routinesPath(memoryDir);
29706
- if (!existsSync37(p))
30021
+ if (!existsSync38(p))
29707
30022
  return { routines: {} };
29708
- const doc2 = yaml.load(readFileSync29(p, "utf8"), { schema: yaml.JSON_SCHEMA }) ?? {};
30023
+ const doc2 = yaml.load(readFileSync30(p, "utf8"), { schema: yaml.JSON_SCHEMA }) ?? {};
29709
30024
  const routines = doc2.routines ?? {};
29710
30025
  for (const [name, r] of Object.entries(routines)) {
29711
30026
  if (!r || typeof r.schedule !== "string" || !r.schedule.trim()) {
@@ -29743,7 +30058,7 @@ var init_routines = __esm(() => {
29743
30058
  });
29744
30059
 
29745
30060
  // src/cli/lifecycle.ts
29746
- import { writeFileSync as writeFileSync20, readFileSync as readFileSync30, existsSync as existsSync38 } from "node:fs";
30061
+ import { writeFileSync as writeFileSync21, readFileSync as readFileSync31, existsSync as existsSync39 } from "node:fs";
29747
30062
  async function handleProposeCommand({ positional, flags }) {
29748
30063
  const ctx = requireRoot(flags);
29749
30064
  if (!ctx)
@@ -29892,7 +30207,7 @@ async function handleExportCommand({ flags }) {
29892
30207
  const snapshot = exportSnapshot(ctx.store);
29893
30208
  const outPath = flags["out"];
29894
30209
  if (outPath) {
29895
- writeFileSync20(outPath, JSON.stringify(snapshot, null, 2), "utf8");
30210
+ writeFileSync21(outPath, JSON.stringify(snapshot, null, 2), "utf8");
29896
30211
  console.log(`exported ${snapshot.total} memories to ${outPath}`);
29897
30212
  } else {
29898
30213
  console.log(JSON.stringify(snapshot, null, 2));
@@ -29906,9 +30221,9 @@ async function handleImportCommand({ positional, flags }) {
29906
30221
  const file = positional[0];
29907
30222
  if (!file)
29908
30223
  return usageError("import requires <file.json>");
29909
- if (!existsSync38(file))
30224
+ if (!existsSync39(file))
29910
30225
  return fail(`error: file ${file} does not exist`);
29911
- const raw = JSON.parse(readFileSync30(file, "utf8"));
30226
+ const raw = JSON.parse(readFileSync31(file, "utf8"));
29912
30227
  const res = importSnapshot(ctx.store, raw, { overwrite: flags["overwrite"] === "true" });
29913
30228
  if (res.errors.length > 0) {
29914
30229
  console.error(`import encountered errors:`);
@@ -30316,6 +30631,9 @@ async function main(argv) {
30316
30631
  return handleSearchTranscriptCommand(parsed);
30317
30632
  case "harvest":
30318
30633
  return handleHarvestCommand(parsed);
30634
+ case "learn":
30635
+ case "sync-chats":
30636
+ return handleLearnCommand(parsed);
30319
30637
  case "harvest-auto":
30320
30638
  return handleHarvestAutoCommand(parsed);
30321
30639
  case "hook":
@@ -30449,6 +30767,7 @@ Ecosystem, Migration & Connectivity:
30449
30767
  agents / detect-agents Scan workstation for 80+ coding agent platforms
30450
30768
  detect Scan machine for 24+ external memory formats
30451
30769
  migrate [--from P] [--all] Ingest external memories with state preservation & secret scrubbing
30770
+ learn / sync-chats [--confirm] Auto-discover and harvest memories from all agent chats across machine
30452
30771
  harvest <file|text> --project P Distill conversation transcripts into structured memory units
30453
30772
  harvest-auto [--from <glob>] [--project P] Harvest inbox transcripts as candidates and archive them
30454
30773
  hook install --git Install the harvester as a git pre-commit hook (never clobbers)
@@ -30465,6 +30784,7 @@ var init_cli = __esm(() => {
30465
30784
  init_shared();
30466
30785
  init_ecosystem();
30467
30786
  init_upgrade();
30787
+ init_harvester();
30468
30788
  init_retrieval3();
30469
30789
  init_persona();
30470
30790
  init_wiki2();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "musememory",
3
- "version": "1.9.0",
3
+ "version": "1.10.0",
4
4
  "description": "Autonomous, self-organizing cognitive memory system for AI agents and agency networks",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",