@fractary/codex 0.12.17 → 0.12.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -7,6 +7,7 @@ var zod = require('zod');
7
7
  var fs4 = require('fs/promises');
8
8
  var util = require('util');
9
9
  var yaml2 = require('js-yaml');
10
+ var fs = require('fs');
10
11
 
11
12
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
12
13
 
@@ -35,6 +36,12 @@ var yaml2__namespace = /*#__PURE__*/_interopNamespace(yaml2);
35
36
 
36
37
  var __defProp = Object.defineProperty;
37
38
  var __getOwnPropNames = Object.getOwnPropertyNames;
39
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
40
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
41
+ }) : x)(function(x) {
42
+ if (typeof require !== "undefined") return require.apply(this, arguments);
43
+ throw Error('Dynamic require of "' + x + '" is not supported');
44
+ });
38
45
  var __esm = (fn, res) => function __init() {
39
46
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
40
47
  };
@@ -540,6 +547,22 @@ var init_built_in = __esm({
540
547
  defaultTtl: exports.TTL.TWO_WEEKS,
541
548
  archiveAfterDays: null,
542
549
  archiveStorage: null
550
+ },
551
+ memory: {
552
+ name: "memory",
553
+ description: "Institutional memory entries (troubleshooting, decisions, patterns)",
554
+ patterns: [
555
+ "memory/**",
556
+ ".fractary/codex/memory/**",
557
+ "**/knowledge-base/**"
558
+ ],
559
+ defaultTtl: exports.TTL.ONE_MONTH,
560
+ archiveAfterDays: 365,
561
+ archiveStorage: "cloud",
562
+ syncPatterns: [
563
+ "memory/**",
564
+ ".fractary/codex/memory/**"
565
+ ]
543
566
  }
544
567
  };
545
568
  exports.DEFAULT_TYPE = {
@@ -6725,6 +6748,583 @@ function createHealthChecker(options = {}) {
6725
6748
  return new HealthChecker(options);
6726
6749
  }
6727
6750
 
6751
+ // src/memory/types.ts
6752
+ var MEMORY_TYPE_PREFIXES = {
6753
+ "troubleshooting": "TS",
6754
+ "architectural-decision": "AD",
6755
+ "performance": "PF",
6756
+ "pattern": "PT",
6757
+ "integration": "IN",
6758
+ "convention": "CV"
6759
+ };
6760
+ var DEFAULT_MEMORY_CONFIG = {
6761
+ memoryDir: ".fractary/codex/memory",
6762
+ cacheDir: ".fractary/codex/cache",
6763
+ syncedMemoryPatterns: [
6764
+ ".fractary/codex/cache/projects/*/*/.fractary/codex/memory/**/*.md"
6765
+ ]
6766
+ };
6767
+ function parseFrontmatter(content) {
6768
+ const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
6769
+ if (!match) return null;
6770
+ const yaml3 = match[1];
6771
+ const frontmatter = {};
6772
+ let currentKey = "";
6773
+ let inArray = false;
6774
+ let arrayValues = [];
6775
+ for (const line of yaml3.split("\n")) {
6776
+ const trimmed = line.trim();
6777
+ if (!trimmed || trimmed.startsWith("#")) continue;
6778
+ if (trimmed.startsWith("- ") && inArray) {
6779
+ let value = trimmed.slice(2).trim();
6780
+ if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
6781
+ value = value.slice(1, -1);
6782
+ }
6783
+ arrayValues.push(value);
6784
+ continue;
6785
+ }
6786
+ if (inArray && currentKey) {
6787
+ frontmatter[currentKey] = arrayValues;
6788
+ inArray = false;
6789
+ arrayValues = [];
6790
+ currentKey = "";
6791
+ }
6792
+ const kvMatch = trimmed.match(/^([a-zA-Z_][a-zA-Z0-9_]*)\s*:\s*(.*)$/);
6793
+ if (kvMatch) {
6794
+ const key = kvMatch[1];
6795
+ let value = kvMatch[2].trim();
6796
+ if (value === "" || value === "[]") {
6797
+ currentKey = key;
6798
+ inArray = true;
6799
+ arrayValues = [];
6800
+ if (value === "[]") {
6801
+ frontmatter[key] = [];
6802
+ inArray = false;
6803
+ currentKey = "";
6804
+ }
6805
+ continue;
6806
+ }
6807
+ if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
6808
+ value = value.slice(1, -1);
6809
+ }
6810
+ if (value === "true") frontmatter[key] = true;
6811
+ else if (value === "false") frontmatter[key] = false;
6812
+ else if (value === "null") frontmatter[key] = null;
6813
+ else if (/^-?\d+$/.test(value)) frontmatter[key] = parseInt(value, 10);
6814
+ else if (/^-?\d+\.\d+$/.test(value)) frontmatter[key] = parseFloat(value);
6815
+ else frontmatter[key] = value;
6816
+ }
6817
+ }
6818
+ if (inArray && currentKey) {
6819
+ frontmatter[currentKey] = arrayValues;
6820
+ }
6821
+ if (!frontmatter.title && !frontmatter.memory_id && !frontmatter.id) {
6822
+ return null;
6823
+ }
6824
+ return frontmatter;
6825
+ }
6826
+ function findMarkdownFiles(dir) {
6827
+ const results = [];
6828
+ if (!fs.existsSync(dir)) return results;
6829
+ try {
6830
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
6831
+ for (const entry of entries) {
6832
+ const fullPath = path3.join(dir, entry.name);
6833
+ if (entry.isDirectory()) {
6834
+ results.push(...findMarkdownFiles(fullPath));
6835
+ } else if (entry.isFile() && entry.name.endsWith(".md")) {
6836
+ results.push(fullPath);
6837
+ }
6838
+ }
6839
+ } catch {
6840
+ }
6841
+ return results;
6842
+ }
6843
+ function findSyncedMemoryFiles(cacheDir) {
6844
+ const results = [];
6845
+ const projectsDir = path3.join(cacheDir, "projects");
6846
+ if (!fs.existsSync(projectsDir)) return results;
6847
+ try {
6848
+ const orgs = fs.readdirSync(projectsDir, { withFileTypes: true });
6849
+ for (const org of orgs) {
6850
+ if (!org.isDirectory()) continue;
6851
+ const orgDir = path3.join(projectsDir, org.name);
6852
+ const projects = fs.readdirSync(orgDir, { withFileTypes: true });
6853
+ for (const project of projects) {
6854
+ if (!project.isDirectory()) continue;
6855
+ const memoryDir = path3.join(orgDir, project.name, ".fractary", "codex", "memory");
6856
+ if (!fs.existsSync(memoryDir)) continue;
6857
+ const files = findMarkdownFiles(memoryDir);
6858
+ const source = `${org.name}/${project.name}`;
6859
+ for (const file of files) {
6860
+ results.push({ path: file, source });
6861
+ }
6862
+ }
6863
+ }
6864
+ } catch {
6865
+ }
6866
+ return results;
6867
+ }
6868
+ var MemorySearcher = class {
6869
+ config;
6870
+ projectRoot;
6871
+ constructor(projectRoot, config) {
6872
+ this.projectRoot = projectRoot;
6873
+ this.config = { ...DEFAULT_MEMORY_CONFIG, ...config };
6874
+ }
6875
+ /**
6876
+ * Search memories with cascading scope
6877
+ *
6878
+ * 1. Project memories (highest priority)
6879
+ * 2. Synced memories from other projects
6880
+ */
6881
+ search(query) {
6882
+ const index = this.loadOrRebuildIndex();
6883
+ let candidates = index.entries.filter((entry) => {
6884
+ const fm = entry.frontmatter;
6885
+ if (query.memory_type && fm.memory_type !== query.memory_type) return false;
6886
+ if (query.category && fm.category !== query.category) return false;
6887
+ if (query.phase) {
6888
+ const phases = fm.phases || (fm.phase ? [fm.phase] : []);
6889
+ if (!phases.includes(query.phase)) return false;
6890
+ }
6891
+ if (query.agent) {
6892
+ const agents = fm.agents || (fm.agent ? [fm.agent] : []);
6893
+ if (!agents.includes(query.agent)) return false;
6894
+ }
6895
+ if (query.tags && query.tags.length > 0) {
6896
+ if (!fm.tags || !query.tags.some((t) => fm.tags.includes(t))) return false;
6897
+ }
6898
+ if (query.status && fm.status !== query.status) return false;
6899
+ return true;
6900
+ });
6901
+ const scored = candidates.map((entry) => ({
6902
+ entry,
6903
+ score: this.calculateRelevanceScore(entry, query),
6904
+ source: entry.source,
6905
+ filePath: entry.file_path
6906
+ }));
6907
+ scored.sort((a, b) => b.score - a.score);
6908
+ const limit = query.limit || 20;
6909
+ return scored.slice(0, limit);
6910
+ }
6911
+ /**
6912
+ * Calculate relevance score for a memory entry
6913
+ */
6914
+ calculateRelevanceScore(entry, query) {
6915
+ const fm = entry.frontmatter;
6916
+ let score = 0;
6917
+ score += Math.min((fm.success_count || 0) * 2, 40);
6918
+ if (fm.status === "verified" || fm.verified === true) {
6919
+ score += 10;
6920
+ }
6921
+ if (query.agent) {
6922
+ const agents = fm.agents || (fm.agent ? [fm.agent] : []);
6923
+ if (agents.includes(query.agent)) {
6924
+ score += 20;
6925
+ }
6926
+ }
6927
+ if (query.phase) {
6928
+ const phases = fm.phases || (fm.phase ? [fm.phase] : []);
6929
+ if (phases.includes(query.phase)) {
6930
+ score += 10;
6931
+ }
6932
+ }
6933
+ score += entry.source === "local" ? 10 : 5;
6934
+ if (query.text) {
6935
+ const queryLower = query.text.toLowerCase();
6936
+ if (fm.symptoms && fm.symptoms.some((s) => s.toLowerCase().includes(queryLower) || queryLower.includes(s.toLowerCase()))) {
6937
+ score += 15;
6938
+ }
6939
+ if (fm.keywords && fm.keywords.some((k) => queryLower.includes(k.toLowerCase()))) {
6940
+ score += 5;
6941
+ }
6942
+ if (fm.description && fm.description.toLowerCase().includes(queryLower)) {
6943
+ score += 8;
6944
+ }
6945
+ if (fm.title && fm.title.toLowerCase().includes(queryLower)) {
6946
+ score += 5;
6947
+ }
6948
+ }
6949
+ return score;
6950
+ }
6951
+ /**
6952
+ * Load the frontmatter index from cache, or rebuild if stale
6953
+ */
6954
+ loadOrRebuildIndex() {
6955
+ const indexPath = path3.join(this.projectRoot, this.config.cacheDir, "memory-index.json");
6956
+ let existingIndex = null;
6957
+ if (fs.existsSync(indexPath)) {
6958
+ try {
6959
+ const raw = fs.readFileSync(indexPath, "utf-8");
6960
+ existingIndex = JSON.parse(raw);
6961
+ } catch {
6962
+ existingIndex = null;
6963
+ }
6964
+ }
6965
+ const memoryDir = path3.join(this.projectRoot, this.config.memoryDir);
6966
+ const localFiles = findMarkdownFiles(memoryDir);
6967
+ const syncedFiles = findSyncedMemoryFiles(path3.join(this.projectRoot, this.config.cacheDir));
6968
+ const needsRebuild = !existingIndex || this.isIndexStale(existingIndex, localFiles, syncedFiles.map((f) => f.path));
6969
+ if (!needsRebuild && existingIndex) {
6970
+ return existingIndex;
6971
+ }
6972
+ return this.rebuildIndex(localFiles, syncedFiles, indexPath);
6973
+ }
6974
+ /**
6975
+ * Check if the index is stale (any source file newer than index)
6976
+ */
6977
+ isIndexStale(index, localFiles, syncedFilePaths) {
6978
+ const indexTime = new Date(index.built_at).getTime();
6979
+ const allFiles = [...localFiles, ...syncedFilePaths];
6980
+ for (const file of allFiles) {
6981
+ try {
6982
+ const stat = fs.statSync(file);
6983
+ if (stat.mtimeMs > indexTime) return true;
6984
+ } catch {
6985
+ }
6986
+ }
6987
+ const indexedPaths = new Set(index.entries.map((e) => e.file_path));
6988
+ for (const file of allFiles) {
6989
+ if (!indexedPaths.has(file)) return true;
6990
+ }
6991
+ return false;
6992
+ }
6993
+ /**
6994
+ * Rebuild the frontmatter index from all memory sources
6995
+ */
6996
+ rebuildIndex(localFiles, syncedFiles, indexPath) {
6997
+ const entries = [];
6998
+ for (const file of localFiles) {
6999
+ const entry = this.indexFile(file, "local");
7000
+ if (entry) entries.push(entry);
7001
+ }
7002
+ for (const { path: path9, source } of syncedFiles) {
7003
+ const entry = this.indexFile(path9, source);
7004
+ if (entry) entries.push(entry);
7005
+ }
7006
+ const index = {
7007
+ version: 1,
7008
+ built_at: (/* @__PURE__ */ new Date()).toISOString(),
7009
+ entries
7010
+ };
7011
+ try {
7012
+ const dir = path3.dirname(indexPath);
7013
+ if (!fs.existsSync(dir)) {
7014
+ fs.mkdirSync(dir, { recursive: true });
7015
+ }
7016
+ fs.writeFileSync(indexPath, JSON.stringify(index, null, 2), "utf-8");
7017
+ } catch {
7018
+ }
7019
+ return index;
7020
+ }
7021
+ /**
7022
+ * Index a single memory file by parsing its frontmatter
7023
+ */
7024
+ indexFile(filePath, source) {
7025
+ try {
7026
+ const content = fs.readFileSync(filePath, "utf-8");
7027
+ const frontmatter = parseFrontmatter(content);
7028
+ if (!frontmatter) return null;
7029
+ const stat = fs.statSync(filePath);
7030
+ return {
7031
+ file_path: filePath,
7032
+ mtime: stat.mtime.toISOString(),
7033
+ source,
7034
+ frontmatter
7035
+ };
7036
+ } catch {
7037
+ return null;
7038
+ }
7039
+ }
7040
+ /**
7041
+ * Invalidate the index cache (call after writing memories)
7042
+ */
7043
+ invalidateIndex() {
7044
+ const indexPath = path3.join(this.projectRoot, this.config.cacheDir, "memory-index.json");
7045
+ try {
7046
+ if (fs.existsSync(indexPath)) {
7047
+ const { unlinkSync } = __require("fs");
7048
+ unlinkSync(indexPath);
7049
+ }
7050
+ } catch {
7051
+ }
7052
+ }
7053
+ };
7054
+ function sanitizeSlug(text, maxLength = 50) {
7055
+ return text.toLowerCase().replace(/[^a-z0-9\s-]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").slice(0, maxLength);
7056
+ }
7057
+ function serializeFrontmatter(fm) {
7058
+ const lines = ["---"];
7059
+ for (const [key, value] of Object.entries(fm)) {
7060
+ if (value === void 0 || value === null) continue;
7061
+ if (Array.isArray(value)) {
7062
+ if (value.length === 0) {
7063
+ lines.push(`${key}: []`);
7064
+ } else {
7065
+ lines.push(`${key}:`);
7066
+ for (const item of value) {
7067
+ const itemStr = typeof item === "string" && (item.includes(":") || item.includes('"') || item.includes("'")) ? `"${item.replace(/"/g, '\\"')}"` : String(item);
7068
+ lines.push(` - ${itemStr}`);
7069
+ }
7070
+ }
7071
+ } else if (typeof value === "string") {
7072
+ if (value.includes(":") || value.includes('"') || value.includes("'") || value.includes("\n")) {
7073
+ lines.push(`${key}: "${value.replace(/"/g, '\\"')}"`);
7074
+ } else {
7075
+ lines.push(`${key}: ${value}`);
7076
+ }
7077
+ } else {
7078
+ lines.push(`${key}: ${value}`);
7079
+ }
7080
+ }
7081
+ lines.push("---");
7082
+ return lines.join("\n");
7083
+ }
7084
+ function getNextSequence(typeDir, prefix) {
7085
+ if (!fs.existsSync(typeDir)) return 1;
7086
+ let maxSeq = 0;
7087
+ try {
7088
+ const files = fs.readdirSync(typeDir);
7089
+ const pattern = new RegExp(`^MEM-${prefix}-(\\d+)-`);
7090
+ for (const file of files) {
7091
+ const match = file.match(pattern);
7092
+ if (match) {
7093
+ const seq = parseInt(match[1], 10);
7094
+ if (seq > maxSeq) maxSeq = seq;
7095
+ }
7096
+ }
7097
+ } catch {
7098
+ }
7099
+ return maxSeq + 1;
7100
+ }
7101
+ function calculateSimilarity(existing, newEntry) {
7102
+ let score = 0;
7103
+ let factors = 0;
7104
+ if (existing.title && newEntry.title) {
7105
+ const existingWords = new Set(existing.title.toLowerCase().split(/\s+/));
7106
+ const newWords = new Set(newEntry.title.toLowerCase().split(/\s+/));
7107
+ const intersection = [...existingWords].filter((w) => newWords.has(w)).length;
7108
+ const union = (/* @__PURE__ */ new Set([...existingWords, ...newWords])).size;
7109
+ score += union > 0 ? intersection / union : 0;
7110
+ factors++;
7111
+ }
7112
+ if (existing.symptoms && newEntry.symptoms) {
7113
+ const existingSymptoms = new Set(existing.symptoms.map((s) => s.toLowerCase()));
7114
+ const newSymptoms = new Set(newEntry.symptoms.map((s) => s.toLowerCase()));
7115
+ const intersection = [...existingSymptoms].filter((s) => newSymptoms.has(s)).length;
7116
+ const union = (/* @__PURE__ */ new Set([...existingSymptoms, ...newSymptoms])).size;
7117
+ score += union > 0 ? intersection / union : 0;
7118
+ factors++;
7119
+ }
7120
+ if (existing.category && newEntry.category) {
7121
+ score += existing.category === newEntry.category ? 1 : 0;
7122
+ factors++;
7123
+ }
7124
+ if (existing.memory_type && newEntry.memory_type) {
7125
+ score += existing.memory_type === newEntry.memory_type ? 1 : 0;
7126
+ factors++;
7127
+ }
7128
+ return factors > 0 ? score / factors : 0;
7129
+ }
7130
+ var MemoryWriter = class {
7131
+ config;
7132
+ projectRoot;
7133
+ searcher;
7134
+ constructor(projectRoot, config) {
7135
+ this.projectRoot = projectRoot;
7136
+ this.config = { ...DEFAULT_MEMORY_CONFIG, ...config };
7137
+ this.searcher = new MemorySearcher(projectRoot, config);
7138
+ }
7139
+ /**
7140
+ * Write a new memory entry
7141
+ *
7142
+ * Handles ID generation, deduplication check, file creation,
7143
+ * and index invalidation.
7144
+ */
7145
+ write(options) {
7146
+ const { memory_type, title, description, body, frontmatter } = options;
7147
+ const dupResult = this.checkDuplicate(frontmatter, memory_type);
7148
+ if (dupResult) {
7149
+ return {
7150
+ memory_id: dupResult.memory_id,
7151
+ file_path: dupResult.file_path,
7152
+ deduplicated: true,
7153
+ existing_id: dupResult.memory_id
7154
+ };
7155
+ }
7156
+ const prefix = MEMORY_TYPE_PREFIXES[memory_type];
7157
+ const typeDir = path3.join(this.projectRoot, this.config.memoryDir, memory_type);
7158
+ const seq = getNextSequence(typeDir, prefix);
7159
+ const slug = sanitizeSlug(title);
7160
+ const memoryId = `MEM-${prefix}-${String(seq).padStart(3, "0")}-${slug}`;
7161
+ const fullFrontmatter = {
7162
+ title,
7163
+ description,
7164
+ memory_type,
7165
+ memory_id: memoryId,
7166
+ status: "draft",
7167
+ created: (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
7168
+ verified: false,
7169
+ success_count: 0,
7170
+ ...frontmatter
7171
+ };
7172
+ fullFrontmatter.memory_type = memory_type;
7173
+ fullFrontmatter.memory_id = memoryId;
7174
+ const fileContent = `${serializeFrontmatter(fullFrontmatter)}
7175
+
7176
+ ${body}
7177
+ `;
7178
+ const fileName = `${memoryId}.md`;
7179
+ const filePath = path3.join(typeDir, fileName);
7180
+ if (!fs.existsSync(typeDir)) {
7181
+ fs.mkdirSync(typeDir, { recursive: true });
7182
+ }
7183
+ fs.writeFileSync(filePath, fileContent, "utf-8");
7184
+ this.searcher.invalidateIndex();
7185
+ return {
7186
+ memory_id: memoryId,
7187
+ file_path: filePath,
7188
+ deduplicated: false
7189
+ };
7190
+ }
7191
+ /**
7192
+ * Update an existing memory's frontmatter
7193
+ */
7194
+ update(memoryId, changes) {
7195
+ const filePath = this.findMemoryFile(memoryId);
7196
+ if (!filePath) {
7197
+ throw new Error(`Memory not found: ${memoryId}`);
7198
+ }
7199
+ const content = fs.readFileSync(filePath, "utf-8");
7200
+ const fmMatch = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/);
7201
+ if (!fmMatch) {
7202
+ throw new Error(`Invalid memory file format: ${filePath}`);
7203
+ }
7204
+ const existingContent = fmMatch[1];
7205
+ const bodyContent = fmMatch[2];
7206
+ const lines = existingContent.split("\n");
7207
+ const updatedFm = {};
7208
+ let currentKey = "";
7209
+ let inArray = false;
7210
+ let arrayValues = [];
7211
+ for (const line of lines) {
7212
+ const trimmed = line.trim();
7213
+ if (!trimmed || trimmed.startsWith("#")) continue;
7214
+ if (trimmed.startsWith("- ") && inArray) {
7215
+ let value = trimmed.slice(2).trim();
7216
+ if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
7217
+ value = value.slice(1, -1);
7218
+ }
7219
+ arrayValues.push(value);
7220
+ continue;
7221
+ }
7222
+ if (inArray && currentKey) {
7223
+ updatedFm[currentKey] = arrayValues;
7224
+ inArray = false;
7225
+ arrayValues = [];
7226
+ currentKey = "";
7227
+ }
7228
+ const kvMatch = trimmed.match(/^([a-zA-Z_][a-zA-Z0-9_]*)\s*:\s*(.*)$/);
7229
+ if (kvMatch) {
7230
+ const key = kvMatch[1];
7231
+ let value = kvMatch[2].trim();
7232
+ if (value === "" || value === "[]") {
7233
+ currentKey = key;
7234
+ inArray = true;
7235
+ arrayValues = [];
7236
+ if (value === "[]") {
7237
+ updatedFm[key] = [];
7238
+ inArray = false;
7239
+ currentKey = "";
7240
+ }
7241
+ continue;
7242
+ }
7243
+ if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
7244
+ value = value.slice(1, -1);
7245
+ }
7246
+ if (value === "true") updatedFm[key] = true;
7247
+ else if (value === "false") updatedFm[key] = false;
7248
+ else if (value === "null") updatedFm[key] = null;
7249
+ else if (/^-?\d+$/.test(value)) updatedFm[key] = parseInt(value, 10);
7250
+ else if (/^-?\d+\.\d+$/.test(value)) updatedFm[key] = parseFloat(value);
7251
+ else updatedFm[key] = value;
7252
+ }
7253
+ }
7254
+ if (inArray && currentKey) {
7255
+ updatedFm[currentKey] = arrayValues;
7256
+ }
7257
+ const merged = { ...updatedFm, ...changes, updated: (/* @__PURE__ */ new Date()).toISOString().split("T")[0] };
7258
+ const newContent = `${serializeFrontmatter(merged)}
7259
+
7260
+ ${bodyContent}`;
7261
+ fs.writeFileSync(filePath, newContent, "utf-8");
7262
+ this.searcher.invalidateIndex();
7263
+ }
7264
+ /**
7265
+ * Deprecate a memory
7266
+ */
7267
+ deprecate(memoryId, reason, supersededBy) {
7268
+ const changes = {
7269
+ status: "deprecated",
7270
+ deprecated_reason: reason
7271
+ };
7272
+ if (supersededBy) {
7273
+ changes.superseded_by = supersededBy;
7274
+ }
7275
+ this.update(memoryId, changes);
7276
+ }
7277
+ /**
7278
+ * Check for duplicate memories (>0.95 similarity)
7279
+ */
7280
+ checkDuplicate(frontmatter, memoryType) {
7281
+ const results = this.searcher.search({
7282
+ memory_type: memoryType,
7283
+ category: frontmatter.category,
7284
+ limit: 10
7285
+ });
7286
+ for (const result of results) {
7287
+ const similarity = calculateSimilarity(result.entry.frontmatter, frontmatter);
7288
+ if (similarity > 0.95) {
7289
+ return {
7290
+ memory_id: result.entry.frontmatter.memory_id,
7291
+ file_path: result.filePath
7292
+ };
7293
+ }
7294
+ }
7295
+ return null;
7296
+ }
7297
+ /**
7298
+ * Find a memory file by its ID
7299
+ */
7300
+ findMemoryFile(memoryId) {
7301
+ const memoryDir = path3.join(this.projectRoot, this.config.memoryDir);
7302
+ if (!fs.existsSync(memoryDir)) return null;
7303
+ const files = findMarkdownFilesRecursive(memoryDir);
7304
+ for (const file of files) {
7305
+ if (file.includes(memoryId)) return file;
7306
+ }
7307
+ return null;
7308
+ }
7309
+ };
7310
+ function findMarkdownFilesRecursive(dir) {
7311
+ const results = [];
7312
+ if (!fs.existsSync(dir)) return results;
7313
+ try {
7314
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
7315
+ for (const entry of entries) {
7316
+ const fullPath = path3.join(dir, entry.name);
7317
+ if (entry.isDirectory()) {
7318
+ results.push(...findMarkdownFilesRecursive(fullPath));
7319
+ } else if (entry.isFile() && entry.name.endsWith(".md")) {
7320
+ results.push(fullPath);
7321
+ }
7322
+ }
7323
+ } catch {
7324
+ }
7325
+ return results;
7326
+ }
7327
+
6728
7328
  // src/client/codex-client.ts
6729
7329
  var CodexClient = class _CodexClient {
6730
7330
  cache;
@@ -7044,11 +7644,15 @@ exports.CommonRules = CommonRules;
7044
7644
  exports.ConfigManager = ConfigManager;
7045
7645
  exports.DEFAULT_FRACTARY_GITIGNORE = DEFAULT_FRACTARY_GITIGNORE;
7046
7646
  exports.DEFAULT_GLOBAL_EXCLUDES = DEFAULT_GLOBAL_EXCLUDES;
7647
+ exports.DEFAULT_MEMORY_CONFIG = DEFAULT_MEMORY_CONFIG;
7047
7648
  exports.DEFAULT_MIGRATION_OPTIONS = DEFAULT_MIGRATION_OPTIONS;
7048
7649
  exports.DEFAULT_PERMISSION_CONFIG = DEFAULT_PERMISSION_CONFIG;
7049
7650
  exports.DEFAULT_SYNC_CONFIG = DEFAULT_SYNC_CONFIG;
7050
7651
  exports.HealthChecker = HealthChecker;
7051
7652
  exports.LEGACY_PATTERNS = LEGACY_PATTERNS;
7653
+ exports.MEMORY_TYPE_PREFIXES = MEMORY_TYPE_PREFIXES;
7654
+ exports.MemorySearcher = MemorySearcher;
7655
+ exports.MemoryWriter = MemoryWriter;
7052
7656
  exports.MetadataSchema = MetadataSchema;
7053
7657
  exports.PERMISSION_LEVEL_ORDER = PERMISSION_LEVEL_ORDER;
7054
7658
  exports.PermissionDeniedError = PermissionDeniedError;