@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/README.md +219 -52
- package/dist/index.cjs +604 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +109 -1
- package/dist/index.d.ts +109 -1
- package/dist/index.js +602 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as path3 from 'path';
|
|
2
|
-
import path3__default from 'path';
|
|
2
|
+
import path3__default, { join, dirname } from 'path';
|
|
3
3
|
import { execFile, execSync } from 'child_process';
|
|
4
4
|
import micromatch3 from 'micromatch';
|
|
5
5
|
import { z } from 'zod';
|
|
@@ -8,9 +8,16 @@ import fs4__default from 'fs/promises';
|
|
|
8
8
|
import { promisify } from 'util';
|
|
9
9
|
import * as yaml2 from 'js-yaml';
|
|
10
10
|
import yaml2__default from 'js-yaml';
|
|
11
|
+
import { existsSync, readFileSync, statSync, mkdirSync, writeFileSync, readdirSync } from 'fs';
|
|
11
12
|
|
|
12
13
|
var __defProp = Object.defineProperty;
|
|
13
14
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
15
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
16
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
17
|
+
}) : x)(function(x) {
|
|
18
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
19
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
20
|
+
});
|
|
14
21
|
var __esm = (fn, res) => function __init() {
|
|
15
22
|
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
16
23
|
};
|
|
@@ -516,6 +523,22 @@ var init_built_in = __esm({
|
|
|
516
523
|
defaultTtl: TTL.TWO_WEEKS,
|
|
517
524
|
archiveAfterDays: null,
|
|
518
525
|
archiveStorage: null
|
|
526
|
+
},
|
|
527
|
+
memory: {
|
|
528
|
+
name: "memory",
|
|
529
|
+
description: "Institutional memory entries (troubleshooting, decisions, patterns)",
|
|
530
|
+
patterns: [
|
|
531
|
+
"memory/**",
|
|
532
|
+
".fractary/codex/memory/**",
|
|
533
|
+
"**/knowledge-base/**"
|
|
534
|
+
],
|
|
535
|
+
defaultTtl: TTL.ONE_MONTH,
|
|
536
|
+
archiveAfterDays: 365,
|
|
537
|
+
archiveStorage: "cloud",
|
|
538
|
+
syncPatterns: [
|
|
539
|
+
"memory/**",
|
|
540
|
+
".fractary/codex/memory/**"
|
|
541
|
+
]
|
|
519
542
|
}
|
|
520
543
|
};
|
|
521
544
|
DEFAULT_TYPE = {
|
|
@@ -6701,6 +6724,583 @@ function createHealthChecker(options = {}) {
|
|
|
6701
6724
|
return new HealthChecker(options);
|
|
6702
6725
|
}
|
|
6703
6726
|
|
|
6727
|
+
// src/memory/types.ts
|
|
6728
|
+
var MEMORY_TYPE_PREFIXES = {
|
|
6729
|
+
"troubleshooting": "TS",
|
|
6730
|
+
"architectural-decision": "AD",
|
|
6731
|
+
"performance": "PF",
|
|
6732
|
+
"pattern": "PT",
|
|
6733
|
+
"integration": "IN",
|
|
6734
|
+
"convention": "CV"
|
|
6735
|
+
};
|
|
6736
|
+
var DEFAULT_MEMORY_CONFIG = {
|
|
6737
|
+
memoryDir: ".fractary/codex/memory",
|
|
6738
|
+
cacheDir: ".fractary/codex/cache",
|
|
6739
|
+
syncedMemoryPatterns: [
|
|
6740
|
+
".fractary/codex/cache/projects/*/*/.fractary/codex/memory/**/*.md"
|
|
6741
|
+
]
|
|
6742
|
+
};
|
|
6743
|
+
function parseFrontmatter(content) {
|
|
6744
|
+
const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
6745
|
+
if (!match) return null;
|
|
6746
|
+
const yaml3 = match[1];
|
|
6747
|
+
const frontmatter = {};
|
|
6748
|
+
let currentKey = "";
|
|
6749
|
+
let inArray = false;
|
|
6750
|
+
let arrayValues = [];
|
|
6751
|
+
for (const line of yaml3.split("\n")) {
|
|
6752
|
+
const trimmed = line.trim();
|
|
6753
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
6754
|
+
if (trimmed.startsWith("- ") && inArray) {
|
|
6755
|
+
let value = trimmed.slice(2).trim();
|
|
6756
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
6757
|
+
value = value.slice(1, -1);
|
|
6758
|
+
}
|
|
6759
|
+
arrayValues.push(value);
|
|
6760
|
+
continue;
|
|
6761
|
+
}
|
|
6762
|
+
if (inArray && currentKey) {
|
|
6763
|
+
frontmatter[currentKey] = arrayValues;
|
|
6764
|
+
inArray = false;
|
|
6765
|
+
arrayValues = [];
|
|
6766
|
+
currentKey = "";
|
|
6767
|
+
}
|
|
6768
|
+
const kvMatch = trimmed.match(/^([a-zA-Z_][a-zA-Z0-9_]*)\s*:\s*(.*)$/);
|
|
6769
|
+
if (kvMatch) {
|
|
6770
|
+
const key = kvMatch[1];
|
|
6771
|
+
let value = kvMatch[2].trim();
|
|
6772
|
+
if (value === "" || value === "[]") {
|
|
6773
|
+
currentKey = key;
|
|
6774
|
+
inArray = true;
|
|
6775
|
+
arrayValues = [];
|
|
6776
|
+
if (value === "[]") {
|
|
6777
|
+
frontmatter[key] = [];
|
|
6778
|
+
inArray = false;
|
|
6779
|
+
currentKey = "";
|
|
6780
|
+
}
|
|
6781
|
+
continue;
|
|
6782
|
+
}
|
|
6783
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
6784
|
+
value = value.slice(1, -1);
|
|
6785
|
+
}
|
|
6786
|
+
if (value === "true") frontmatter[key] = true;
|
|
6787
|
+
else if (value === "false") frontmatter[key] = false;
|
|
6788
|
+
else if (value === "null") frontmatter[key] = null;
|
|
6789
|
+
else if (/^-?\d+$/.test(value)) frontmatter[key] = parseInt(value, 10);
|
|
6790
|
+
else if (/^-?\d+\.\d+$/.test(value)) frontmatter[key] = parseFloat(value);
|
|
6791
|
+
else frontmatter[key] = value;
|
|
6792
|
+
}
|
|
6793
|
+
}
|
|
6794
|
+
if (inArray && currentKey) {
|
|
6795
|
+
frontmatter[currentKey] = arrayValues;
|
|
6796
|
+
}
|
|
6797
|
+
if (!frontmatter.title && !frontmatter.memory_id && !frontmatter.id) {
|
|
6798
|
+
return null;
|
|
6799
|
+
}
|
|
6800
|
+
return frontmatter;
|
|
6801
|
+
}
|
|
6802
|
+
function findMarkdownFiles(dir) {
|
|
6803
|
+
const results = [];
|
|
6804
|
+
if (!existsSync(dir)) return results;
|
|
6805
|
+
try {
|
|
6806
|
+
const entries = readdirSync(dir, { withFileTypes: true });
|
|
6807
|
+
for (const entry of entries) {
|
|
6808
|
+
const fullPath = join(dir, entry.name);
|
|
6809
|
+
if (entry.isDirectory()) {
|
|
6810
|
+
results.push(...findMarkdownFiles(fullPath));
|
|
6811
|
+
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
6812
|
+
results.push(fullPath);
|
|
6813
|
+
}
|
|
6814
|
+
}
|
|
6815
|
+
} catch {
|
|
6816
|
+
}
|
|
6817
|
+
return results;
|
|
6818
|
+
}
|
|
6819
|
+
function findSyncedMemoryFiles(cacheDir) {
|
|
6820
|
+
const results = [];
|
|
6821
|
+
const projectsDir = join(cacheDir, "projects");
|
|
6822
|
+
if (!existsSync(projectsDir)) return results;
|
|
6823
|
+
try {
|
|
6824
|
+
const orgs = readdirSync(projectsDir, { withFileTypes: true });
|
|
6825
|
+
for (const org of orgs) {
|
|
6826
|
+
if (!org.isDirectory()) continue;
|
|
6827
|
+
const orgDir = join(projectsDir, org.name);
|
|
6828
|
+
const projects = readdirSync(orgDir, { withFileTypes: true });
|
|
6829
|
+
for (const project of projects) {
|
|
6830
|
+
if (!project.isDirectory()) continue;
|
|
6831
|
+
const memoryDir = join(orgDir, project.name, ".fractary", "codex", "memory");
|
|
6832
|
+
if (!existsSync(memoryDir)) continue;
|
|
6833
|
+
const files = findMarkdownFiles(memoryDir);
|
|
6834
|
+
const source = `${org.name}/${project.name}`;
|
|
6835
|
+
for (const file of files) {
|
|
6836
|
+
results.push({ path: file, source });
|
|
6837
|
+
}
|
|
6838
|
+
}
|
|
6839
|
+
}
|
|
6840
|
+
} catch {
|
|
6841
|
+
}
|
|
6842
|
+
return results;
|
|
6843
|
+
}
|
|
6844
|
+
var MemorySearcher = class {
|
|
6845
|
+
config;
|
|
6846
|
+
projectRoot;
|
|
6847
|
+
constructor(projectRoot, config) {
|
|
6848
|
+
this.projectRoot = projectRoot;
|
|
6849
|
+
this.config = { ...DEFAULT_MEMORY_CONFIG, ...config };
|
|
6850
|
+
}
|
|
6851
|
+
/**
|
|
6852
|
+
* Search memories with cascading scope
|
|
6853
|
+
*
|
|
6854
|
+
* 1. Project memories (highest priority)
|
|
6855
|
+
* 2. Synced memories from other projects
|
|
6856
|
+
*/
|
|
6857
|
+
search(query) {
|
|
6858
|
+
const index = this.loadOrRebuildIndex();
|
|
6859
|
+
let candidates = index.entries.filter((entry) => {
|
|
6860
|
+
const fm = entry.frontmatter;
|
|
6861
|
+
if (query.memory_type && fm.memory_type !== query.memory_type) return false;
|
|
6862
|
+
if (query.category && fm.category !== query.category) return false;
|
|
6863
|
+
if (query.phase) {
|
|
6864
|
+
const phases = fm.phases || (fm.phase ? [fm.phase] : []);
|
|
6865
|
+
if (!phases.includes(query.phase)) return false;
|
|
6866
|
+
}
|
|
6867
|
+
if (query.agent) {
|
|
6868
|
+
const agents = fm.agents || (fm.agent ? [fm.agent] : []);
|
|
6869
|
+
if (!agents.includes(query.agent)) return false;
|
|
6870
|
+
}
|
|
6871
|
+
if (query.tags && query.tags.length > 0) {
|
|
6872
|
+
if (!fm.tags || !query.tags.some((t) => fm.tags.includes(t))) return false;
|
|
6873
|
+
}
|
|
6874
|
+
if (query.status && fm.status !== query.status) return false;
|
|
6875
|
+
return true;
|
|
6876
|
+
});
|
|
6877
|
+
const scored = candidates.map((entry) => ({
|
|
6878
|
+
entry,
|
|
6879
|
+
score: this.calculateRelevanceScore(entry, query),
|
|
6880
|
+
source: entry.source,
|
|
6881
|
+
filePath: entry.file_path
|
|
6882
|
+
}));
|
|
6883
|
+
scored.sort((a, b) => b.score - a.score);
|
|
6884
|
+
const limit = query.limit || 20;
|
|
6885
|
+
return scored.slice(0, limit);
|
|
6886
|
+
}
|
|
6887
|
+
/**
|
|
6888
|
+
* Calculate relevance score for a memory entry
|
|
6889
|
+
*/
|
|
6890
|
+
calculateRelevanceScore(entry, query) {
|
|
6891
|
+
const fm = entry.frontmatter;
|
|
6892
|
+
let score = 0;
|
|
6893
|
+
score += Math.min((fm.success_count || 0) * 2, 40);
|
|
6894
|
+
if (fm.status === "verified" || fm.verified === true) {
|
|
6895
|
+
score += 10;
|
|
6896
|
+
}
|
|
6897
|
+
if (query.agent) {
|
|
6898
|
+
const agents = fm.agents || (fm.agent ? [fm.agent] : []);
|
|
6899
|
+
if (agents.includes(query.agent)) {
|
|
6900
|
+
score += 20;
|
|
6901
|
+
}
|
|
6902
|
+
}
|
|
6903
|
+
if (query.phase) {
|
|
6904
|
+
const phases = fm.phases || (fm.phase ? [fm.phase] : []);
|
|
6905
|
+
if (phases.includes(query.phase)) {
|
|
6906
|
+
score += 10;
|
|
6907
|
+
}
|
|
6908
|
+
}
|
|
6909
|
+
score += entry.source === "local" ? 10 : 5;
|
|
6910
|
+
if (query.text) {
|
|
6911
|
+
const queryLower = query.text.toLowerCase();
|
|
6912
|
+
if (fm.symptoms && fm.symptoms.some((s) => s.toLowerCase().includes(queryLower) || queryLower.includes(s.toLowerCase()))) {
|
|
6913
|
+
score += 15;
|
|
6914
|
+
}
|
|
6915
|
+
if (fm.keywords && fm.keywords.some((k) => queryLower.includes(k.toLowerCase()))) {
|
|
6916
|
+
score += 5;
|
|
6917
|
+
}
|
|
6918
|
+
if (fm.description && fm.description.toLowerCase().includes(queryLower)) {
|
|
6919
|
+
score += 8;
|
|
6920
|
+
}
|
|
6921
|
+
if (fm.title && fm.title.toLowerCase().includes(queryLower)) {
|
|
6922
|
+
score += 5;
|
|
6923
|
+
}
|
|
6924
|
+
}
|
|
6925
|
+
return score;
|
|
6926
|
+
}
|
|
6927
|
+
/**
|
|
6928
|
+
* Load the frontmatter index from cache, or rebuild if stale
|
|
6929
|
+
*/
|
|
6930
|
+
loadOrRebuildIndex() {
|
|
6931
|
+
const indexPath = join(this.projectRoot, this.config.cacheDir, "memory-index.json");
|
|
6932
|
+
let existingIndex = null;
|
|
6933
|
+
if (existsSync(indexPath)) {
|
|
6934
|
+
try {
|
|
6935
|
+
const raw = readFileSync(indexPath, "utf-8");
|
|
6936
|
+
existingIndex = JSON.parse(raw);
|
|
6937
|
+
} catch {
|
|
6938
|
+
existingIndex = null;
|
|
6939
|
+
}
|
|
6940
|
+
}
|
|
6941
|
+
const memoryDir = join(this.projectRoot, this.config.memoryDir);
|
|
6942
|
+
const localFiles = findMarkdownFiles(memoryDir);
|
|
6943
|
+
const syncedFiles = findSyncedMemoryFiles(join(this.projectRoot, this.config.cacheDir));
|
|
6944
|
+
const needsRebuild = !existingIndex || this.isIndexStale(existingIndex, localFiles, syncedFiles.map((f) => f.path));
|
|
6945
|
+
if (!needsRebuild && existingIndex) {
|
|
6946
|
+
return existingIndex;
|
|
6947
|
+
}
|
|
6948
|
+
return this.rebuildIndex(localFiles, syncedFiles, indexPath);
|
|
6949
|
+
}
|
|
6950
|
+
/**
|
|
6951
|
+
* Check if the index is stale (any source file newer than index)
|
|
6952
|
+
*/
|
|
6953
|
+
isIndexStale(index, localFiles, syncedFilePaths) {
|
|
6954
|
+
const indexTime = new Date(index.built_at).getTime();
|
|
6955
|
+
const allFiles = [...localFiles, ...syncedFilePaths];
|
|
6956
|
+
for (const file of allFiles) {
|
|
6957
|
+
try {
|
|
6958
|
+
const stat = statSync(file);
|
|
6959
|
+
if (stat.mtimeMs > indexTime) return true;
|
|
6960
|
+
} catch {
|
|
6961
|
+
}
|
|
6962
|
+
}
|
|
6963
|
+
const indexedPaths = new Set(index.entries.map((e) => e.file_path));
|
|
6964
|
+
for (const file of allFiles) {
|
|
6965
|
+
if (!indexedPaths.has(file)) return true;
|
|
6966
|
+
}
|
|
6967
|
+
return false;
|
|
6968
|
+
}
|
|
6969
|
+
/**
|
|
6970
|
+
* Rebuild the frontmatter index from all memory sources
|
|
6971
|
+
*/
|
|
6972
|
+
rebuildIndex(localFiles, syncedFiles, indexPath) {
|
|
6973
|
+
const entries = [];
|
|
6974
|
+
for (const file of localFiles) {
|
|
6975
|
+
const entry = this.indexFile(file, "local");
|
|
6976
|
+
if (entry) entries.push(entry);
|
|
6977
|
+
}
|
|
6978
|
+
for (const { path: path9, source } of syncedFiles) {
|
|
6979
|
+
const entry = this.indexFile(path9, source);
|
|
6980
|
+
if (entry) entries.push(entry);
|
|
6981
|
+
}
|
|
6982
|
+
const index = {
|
|
6983
|
+
version: 1,
|
|
6984
|
+
built_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6985
|
+
entries
|
|
6986
|
+
};
|
|
6987
|
+
try {
|
|
6988
|
+
const dir = dirname(indexPath);
|
|
6989
|
+
if (!existsSync(dir)) {
|
|
6990
|
+
mkdirSync(dir, { recursive: true });
|
|
6991
|
+
}
|
|
6992
|
+
writeFileSync(indexPath, JSON.stringify(index, null, 2), "utf-8");
|
|
6993
|
+
} catch {
|
|
6994
|
+
}
|
|
6995
|
+
return index;
|
|
6996
|
+
}
|
|
6997
|
+
/**
|
|
6998
|
+
* Index a single memory file by parsing its frontmatter
|
|
6999
|
+
*/
|
|
7000
|
+
indexFile(filePath, source) {
|
|
7001
|
+
try {
|
|
7002
|
+
const content = readFileSync(filePath, "utf-8");
|
|
7003
|
+
const frontmatter = parseFrontmatter(content);
|
|
7004
|
+
if (!frontmatter) return null;
|
|
7005
|
+
const stat = statSync(filePath);
|
|
7006
|
+
return {
|
|
7007
|
+
file_path: filePath,
|
|
7008
|
+
mtime: stat.mtime.toISOString(),
|
|
7009
|
+
source,
|
|
7010
|
+
frontmatter
|
|
7011
|
+
};
|
|
7012
|
+
} catch {
|
|
7013
|
+
return null;
|
|
7014
|
+
}
|
|
7015
|
+
}
|
|
7016
|
+
/**
|
|
7017
|
+
* Invalidate the index cache (call after writing memories)
|
|
7018
|
+
*/
|
|
7019
|
+
invalidateIndex() {
|
|
7020
|
+
const indexPath = join(this.projectRoot, this.config.cacheDir, "memory-index.json");
|
|
7021
|
+
try {
|
|
7022
|
+
if (existsSync(indexPath)) {
|
|
7023
|
+
const { unlinkSync } = __require("fs");
|
|
7024
|
+
unlinkSync(indexPath);
|
|
7025
|
+
}
|
|
7026
|
+
} catch {
|
|
7027
|
+
}
|
|
7028
|
+
}
|
|
7029
|
+
};
|
|
7030
|
+
function sanitizeSlug(text, maxLength = 50) {
|
|
7031
|
+
return text.toLowerCase().replace(/[^a-z0-9\s-]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").slice(0, maxLength);
|
|
7032
|
+
}
|
|
7033
|
+
function serializeFrontmatter(fm) {
|
|
7034
|
+
const lines = ["---"];
|
|
7035
|
+
for (const [key, value] of Object.entries(fm)) {
|
|
7036
|
+
if (value === void 0 || value === null) continue;
|
|
7037
|
+
if (Array.isArray(value)) {
|
|
7038
|
+
if (value.length === 0) {
|
|
7039
|
+
lines.push(`${key}: []`);
|
|
7040
|
+
} else {
|
|
7041
|
+
lines.push(`${key}:`);
|
|
7042
|
+
for (const item of value) {
|
|
7043
|
+
const itemStr = typeof item === "string" && (item.includes(":") || item.includes('"') || item.includes("'")) ? `"${item.replace(/"/g, '\\"')}"` : String(item);
|
|
7044
|
+
lines.push(` - ${itemStr}`);
|
|
7045
|
+
}
|
|
7046
|
+
}
|
|
7047
|
+
} else if (typeof value === "string") {
|
|
7048
|
+
if (value.includes(":") || value.includes('"') || value.includes("'") || value.includes("\n")) {
|
|
7049
|
+
lines.push(`${key}: "${value.replace(/"/g, '\\"')}"`);
|
|
7050
|
+
} else {
|
|
7051
|
+
lines.push(`${key}: ${value}`);
|
|
7052
|
+
}
|
|
7053
|
+
} else {
|
|
7054
|
+
lines.push(`${key}: ${value}`);
|
|
7055
|
+
}
|
|
7056
|
+
}
|
|
7057
|
+
lines.push("---");
|
|
7058
|
+
return lines.join("\n");
|
|
7059
|
+
}
|
|
7060
|
+
function getNextSequence(typeDir, prefix) {
|
|
7061
|
+
if (!existsSync(typeDir)) return 1;
|
|
7062
|
+
let maxSeq = 0;
|
|
7063
|
+
try {
|
|
7064
|
+
const files = readdirSync(typeDir);
|
|
7065
|
+
const pattern = new RegExp(`^MEM-${prefix}-(\\d+)-`);
|
|
7066
|
+
for (const file of files) {
|
|
7067
|
+
const match = file.match(pattern);
|
|
7068
|
+
if (match) {
|
|
7069
|
+
const seq = parseInt(match[1], 10);
|
|
7070
|
+
if (seq > maxSeq) maxSeq = seq;
|
|
7071
|
+
}
|
|
7072
|
+
}
|
|
7073
|
+
} catch {
|
|
7074
|
+
}
|
|
7075
|
+
return maxSeq + 1;
|
|
7076
|
+
}
|
|
7077
|
+
function calculateSimilarity(existing, newEntry) {
|
|
7078
|
+
let score = 0;
|
|
7079
|
+
let factors = 0;
|
|
7080
|
+
if (existing.title && newEntry.title) {
|
|
7081
|
+
const existingWords = new Set(existing.title.toLowerCase().split(/\s+/));
|
|
7082
|
+
const newWords = new Set(newEntry.title.toLowerCase().split(/\s+/));
|
|
7083
|
+
const intersection = [...existingWords].filter((w) => newWords.has(w)).length;
|
|
7084
|
+
const union = (/* @__PURE__ */ new Set([...existingWords, ...newWords])).size;
|
|
7085
|
+
score += union > 0 ? intersection / union : 0;
|
|
7086
|
+
factors++;
|
|
7087
|
+
}
|
|
7088
|
+
if (existing.symptoms && newEntry.symptoms) {
|
|
7089
|
+
const existingSymptoms = new Set(existing.symptoms.map((s) => s.toLowerCase()));
|
|
7090
|
+
const newSymptoms = new Set(newEntry.symptoms.map((s) => s.toLowerCase()));
|
|
7091
|
+
const intersection = [...existingSymptoms].filter((s) => newSymptoms.has(s)).length;
|
|
7092
|
+
const union = (/* @__PURE__ */ new Set([...existingSymptoms, ...newSymptoms])).size;
|
|
7093
|
+
score += union > 0 ? intersection / union : 0;
|
|
7094
|
+
factors++;
|
|
7095
|
+
}
|
|
7096
|
+
if (existing.category && newEntry.category) {
|
|
7097
|
+
score += existing.category === newEntry.category ? 1 : 0;
|
|
7098
|
+
factors++;
|
|
7099
|
+
}
|
|
7100
|
+
if (existing.memory_type && newEntry.memory_type) {
|
|
7101
|
+
score += existing.memory_type === newEntry.memory_type ? 1 : 0;
|
|
7102
|
+
factors++;
|
|
7103
|
+
}
|
|
7104
|
+
return factors > 0 ? score / factors : 0;
|
|
7105
|
+
}
|
|
7106
|
+
var MemoryWriter = class {
|
|
7107
|
+
config;
|
|
7108
|
+
projectRoot;
|
|
7109
|
+
searcher;
|
|
7110
|
+
constructor(projectRoot, config) {
|
|
7111
|
+
this.projectRoot = projectRoot;
|
|
7112
|
+
this.config = { ...DEFAULT_MEMORY_CONFIG, ...config };
|
|
7113
|
+
this.searcher = new MemorySearcher(projectRoot, config);
|
|
7114
|
+
}
|
|
7115
|
+
/**
|
|
7116
|
+
* Write a new memory entry
|
|
7117
|
+
*
|
|
7118
|
+
* Handles ID generation, deduplication check, file creation,
|
|
7119
|
+
* and index invalidation.
|
|
7120
|
+
*/
|
|
7121
|
+
write(options) {
|
|
7122
|
+
const { memory_type, title, description, body, frontmatter } = options;
|
|
7123
|
+
const dupResult = this.checkDuplicate(frontmatter, memory_type);
|
|
7124
|
+
if (dupResult) {
|
|
7125
|
+
return {
|
|
7126
|
+
memory_id: dupResult.memory_id,
|
|
7127
|
+
file_path: dupResult.file_path,
|
|
7128
|
+
deduplicated: true,
|
|
7129
|
+
existing_id: dupResult.memory_id
|
|
7130
|
+
};
|
|
7131
|
+
}
|
|
7132
|
+
const prefix = MEMORY_TYPE_PREFIXES[memory_type];
|
|
7133
|
+
const typeDir = join(this.projectRoot, this.config.memoryDir, memory_type);
|
|
7134
|
+
const seq = getNextSequence(typeDir, prefix);
|
|
7135
|
+
const slug = sanitizeSlug(title);
|
|
7136
|
+
const memoryId = `MEM-${prefix}-${String(seq).padStart(3, "0")}-${slug}`;
|
|
7137
|
+
const fullFrontmatter = {
|
|
7138
|
+
title,
|
|
7139
|
+
description,
|
|
7140
|
+
memory_type,
|
|
7141
|
+
memory_id: memoryId,
|
|
7142
|
+
status: "draft",
|
|
7143
|
+
created: (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
|
|
7144
|
+
verified: false,
|
|
7145
|
+
success_count: 0,
|
|
7146
|
+
...frontmatter
|
|
7147
|
+
};
|
|
7148
|
+
fullFrontmatter.memory_type = memory_type;
|
|
7149
|
+
fullFrontmatter.memory_id = memoryId;
|
|
7150
|
+
const fileContent = `${serializeFrontmatter(fullFrontmatter)}
|
|
7151
|
+
|
|
7152
|
+
${body}
|
|
7153
|
+
`;
|
|
7154
|
+
const fileName = `${memoryId}.md`;
|
|
7155
|
+
const filePath = join(typeDir, fileName);
|
|
7156
|
+
if (!existsSync(typeDir)) {
|
|
7157
|
+
mkdirSync(typeDir, { recursive: true });
|
|
7158
|
+
}
|
|
7159
|
+
writeFileSync(filePath, fileContent, "utf-8");
|
|
7160
|
+
this.searcher.invalidateIndex();
|
|
7161
|
+
return {
|
|
7162
|
+
memory_id: memoryId,
|
|
7163
|
+
file_path: filePath,
|
|
7164
|
+
deduplicated: false
|
|
7165
|
+
};
|
|
7166
|
+
}
|
|
7167
|
+
/**
|
|
7168
|
+
* Update an existing memory's frontmatter
|
|
7169
|
+
*/
|
|
7170
|
+
update(memoryId, changes) {
|
|
7171
|
+
const filePath = this.findMemoryFile(memoryId);
|
|
7172
|
+
if (!filePath) {
|
|
7173
|
+
throw new Error(`Memory not found: ${memoryId}`);
|
|
7174
|
+
}
|
|
7175
|
+
const content = readFileSync(filePath, "utf-8");
|
|
7176
|
+
const fmMatch = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/);
|
|
7177
|
+
if (!fmMatch) {
|
|
7178
|
+
throw new Error(`Invalid memory file format: ${filePath}`);
|
|
7179
|
+
}
|
|
7180
|
+
const existingContent = fmMatch[1];
|
|
7181
|
+
const bodyContent = fmMatch[2];
|
|
7182
|
+
const lines = existingContent.split("\n");
|
|
7183
|
+
const updatedFm = {};
|
|
7184
|
+
let currentKey = "";
|
|
7185
|
+
let inArray = false;
|
|
7186
|
+
let arrayValues = [];
|
|
7187
|
+
for (const line of lines) {
|
|
7188
|
+
const trimmed = line.trim();
|
|
7189
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
7190
|
+
if (trimmed.startsWith("- ") && inArray) {
|
|
7191
|
+
let value = trimmed.slice(2).trim();
|
|
7192
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
7193
|
+
value = value.slice(1, -1);
|
|
7194
|
+
}
|
|
7195
|
+
arrayValues.push(value);
|
|
7196
|
+
continue;
|
|
7197
|
+
}
|
|
7198
|
+
if (inArray && currentKey) {
|
|
7199
|
+
updatedFm[currentKey] = arrayValues;
|
|
7200
|
+
inArray = false;
|
|
7201
|
+
arrayValues = [];
|
|
7202
|
+
currentKey = "";
|
|
7203
|
+
}
|
|
7204
|
+
const kvMatch = trimmed.match(/^([a-zA-Z_][a-zA-Z0-9_]*)\s*:\s*(.*)$/);
|
|
7205
|
+
if (kvMatch) {
|
|
7206
|
+
const key = kvMatch[1];
|
|
7207
|
+
let value = kvMatch[2].trim();
|
|
7208
|
+
if (value === "" || value === "[]") {
|
|
7209
|
+
currentKey = key;
|
|
7210
|
+
inArray = true;
|
|
7211
|
+
arrayValues = [];
|
|
7212
|
+
if (value === "[]") {
|
|
7213
|
+
updatedFm[key] = [];
|
|
7214
|
+
inArray = false;
|
|
7215
|
+
currentKey = "";
|
|
7216
|
+
}
|
|
7217
|
+
continue;
|
|
7218
|
+
}
|
|
7219
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
7220
|
+
value = value.slice(1, -1);
|
|
7221
|
+
}
|
|
7222
|
+
if (value === "true") updatedFm[key] = true;
|
|
7223
|
+
else if (value === "false") updatedFm[key] = false;
|
|
7224
|
+
else if (value === "null") updatedFm[key] = null;
|
|
7225
|
+
else if (/^-?\d+$/.test(value)) updatedFm[key] = parseInt(value, 10);
|
|
7226
|
+
else if (/^-?\d+\.\d+$/.test(value)) updatedFm[key] = parseFloat(value);
|
|
7227
|
+
else updatedFm[key] = value;
|
|
7228
|
+
}
|
|
7229
|
+
}
|
|
7230
|
+
if (inArray && currentKey) {
|
|
7231
|
+
updatedFm[currentKey] = arrayValues;
|
|
7232
|
+
}
|
|
7233
|
+
const merged = { ...updatedFm, ...changes, updated: (/* @__PURE__ */ new Date()).toISOString().split("T")[0] };
|
|
7234
|
+
const newContent = `${serializeFrontmatter(merged)}
|
|
7235
|
+
|
|
7236
|
+
${bodyContent}`;
|
|
7237
|
+
writeFileSync(filePath, newContent, "utf-8");
|
|
7238
|
+
this.searcher.invalidateIndex();
|
|
7239
|
+
}
|
|
7240
|
+
/**
|
|
7241
|
+
* Deprecate a memory
|
|
7242
|
+
*/
|
|
7243
|
+
deprecate(memoryId, reason, supersededBy) {
|
|
7244
|
+
const changes = {
|
|
7245
|
+
status: "deprecated",
|
|
7246
|
+
deprecated_reason: reason
|
|
7247
|
+
};
|
|
7248
|
+
if (supersededBy) {
|
|
7249
|
+
changes.superseded_by = supersededBy;
|
|
7250
|
+
}
|
|
7251
|
+
this.update(memoryId, changes);
|
|
7252
|
+
}
|
|
7253
|
+
/**
|
|
7254
|
+
* Check for duplicate memories (>0.95 similarity)
|
|
7255
|
+
*/
|
|
7256
|
+
checkDuplicate(frontmatter, memoryType) {
|
|
7257
|
+
const results = this.searcher.search({
|
|
7258
|
+
memory_type: memoryType,
|
|
7259
|
+
category: frontmatter.category,
|
|
7260
|
+
limit: 10
|
|
7261
|
+
});
|
|
7262
|
+
for (const result of results) {
|
|
7263
|
+
const similarity = calculateSimilarity(result.entry.frontmatter, frontmatter);
|
|
7264
|
+
if (similarity > 0.95) {
|
|
7265
|
+
return {
|
|
7266
|
+
memory_id: result.entry.frontmatter.memory_id,
|
|
7267
|
+
file_path: result.filePath
|
|
7268
|
+
};
|
|
7269
|
+
}
|
|
7270
|
+
}
|
|
7271
|
+
return null;
|
|
7272
|
+
}
|
|
7273
|
+
/**
|
|
7274
|
+
* Find a memory file by its ID
|
|
7275
|
+
*/
|
|
7276
|
+
findMemoryFile(memoryId) {
|
|
7277
|
+
const memoryDir = join(this.projectRoot, this.config.memoryDir);
|
|
7278
|
+
if (!existsSync(memoryDir)) return null;
|
|
7279
|
+
const files = findMarkdownFilesRecursive(memoryDir);
|
|
7280
|
+
for (const file of files) {
|
|
7281
|
+
if (file.includes(memoryId)) return file;
|
|
7282
|
+
}
|
|
7283
|
+
return null;
|
|
7284
|
+
}
|
|
7285
|
+
};
|
|
7286
|
+
function findMarkdownFilesRecursive(dir) {
|
|
7287
|
+
const results = [];
|
|
7288
|
+
if (!existsSync(dir)) return results;
|
|
7289
|
+
try {
|
|
7290
|
+
const entries = readdirSync(dir, { withFileTypes: true });
|
|
7291
|
+
for (const entry of entries) {
|
|
7292
|
+
const fullPath = join(dir, entry.name);
|
|
7293
|
+
if (entry.isDirectory()) {
|
|
7294
|
+
results.push(...findMarkdownFilesRecursive(fullPath));
|
|
7295
|
+
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
7296
|
+
results.push(fullPath);
|
|
7297
|
+
}
|
|
7298
|
+
}
|
|
7299
|
+
} catch {
|
|
7300
|
+
}
|
|
7301
|
+
return results;
|
|
7302
|
+
}
|
|
7303
|
+
|
|
6704
7304
|
// src/client/codex-client.ts
|
|
6705
7305
|
var CodexClient = class _CodexClient {
|
|
6706
7306
|
cache;
|
|
@@ -7011,6 +7611,6 @@ async function createCodexClient(options) {
|
|
|
7011
7611
|
return CodexClient.create(options);
|
|
7012
7612
|
}
|
|
7013
7613
|
|
|
7014
|
-
export { AutoSyncPatternSchema, BUILT_IN_TYPES, CODEX_DIRECTORIES, CODEX_URI_PREFIX, CONFIG_SCHEMA_VERSION, CacheManager, CachePersistence, CodexClient, CodexConfigSchema, CodexError, CommonRules, ConfigManager, ConfigurationError, CustomTypeSchema, DEFAULT_CACHE_DIR, DEFAULT_FETCH_OPTIONS, DEFAULT_FRACTARY_GITIGNORE, DEFAULT_GLOBAL_EXCLUDES, DEFAULT_MIGRATION_OPTIONS, DEFAULT_PERMISSION_CONFIG, DEFAULT_SYNC_CONFIG, DEFAULT_TYPE, FilePluginFileNotFoundError, FilePluginStorage, GitHubStorage, HealthChecker, HttpStorage, LEGACY_PATTERNS, LEGACY_REF_PREFIX, LocalStorage, MetadataSchema, PERMISSION_LEVEL_ORDER, PermissionDeniedError, PermissionManager, STANDARD_DIRECTORIES, SYNC_PATTERN_PRESETS, StorageManager, SyncManager, SyncRulesSchema, TTL, TypeRegistry, TypesConfigSchema, ValidationError, buildUri, calculateCachePath, calculateContentHash, convertLegacyReference, convertLegacyReferences, convertToUri, createCacheEntry, createCacheManager, createCachePersistence, createCodexClient, createConfigManager, createDefaultRegistry, createEmptyModernConfig, createEmptySyncPlan, createGitHubStorage, createHealthChecker, createHttpStorage, createLocalStorage, createPermissionManager, createRule, createRulesFromPatterns, createStorageManager, createSyncManager, createSyncPlan, deserializeCacheEntry, detectContentType, detectCurrentProject, detectOrganizationFromGit, detectProjectName, detectVersion, discoverCodexRepo, ensureCachePathIgnored, ensureDirectoryStructure, estimateSyncTime, evaluatePath, evaluatePaths, evaluatePatterns, evaluatePermission, evaluatePermissions, expandEnvVars, expandEnvVarsInConfig, extendType, extractEnvVarNames, extractOrgFromRepoName, extractRawFrontmatter, filterByPatterns, filterByPermission, filterPlanOperations, filterSyncablePaths, findLegacyReferences, formatBytes2 as formatBytes, formatDuration, formatPlanSummary, formatSeconds, generateCodexSection, generateMigrationReport, generateReferenceMigrationSummary, generateSyncConfigFromPreset, generateUnifiedConfig, getBuiltInType, getBuiltInTypeNames, getCacheEntryAge, getCacheEntryStatus, getCurrentContext, getCustomSyncDestinations, getDefaultCacheManager, getDefaultConfig, getDefaultDirectories, getDefaultPermissionManager, getDefaultRules, getDefaultStorageManager, getDirectory, getExtension, getFilename, getMigrationRequirements, getPlanStats, getRelativeCachePath, getRemainingTtl, getSyncPreset, getSyncPresetNames, getTargetRepos, hasContentChanged, hasEnvVars, hasFrontmatter, hasLegacyReferences, hasPermission as hasPermissionLevel, installMcpServer, isBuiltInType, isCacheEntryFresh, isCacheEntryValid, isCurrentProjectUri, isLegacyConfig, isLegacyReference, isModernConfig, isAllowed as isPermissionAllowed, isUnifiedConfig, isValidDuration, isValidSize, isValidUri, levelGrants, loadConfig, loadCustomTypes, matchAnyPattern, matchPattern, maxLevel, mergeFetchOptions, mergeRules, mergeTypes, mergeUnifiedConfigs, migrateConfig, migrateFileReferences, minLevel, needsMigration, normalizeCachePath, parseCustomDestination, parseDuration, parseMetadata, parseReference, parseSize, parseTtl, readCodexConfig, readUnifiedConfig, readUnifiedConfig2 as readYamlUnifiedConfig, resolveOrganization, resolveReference, resolveReferences, ruleMatchesAction, ruleMatchesContext, ruleMatchesPath, sanitizeForS3BucketName, sanitizePath, serializeCacheEntry, setDefaultCacheManager, setDefaultPermissionManager, setDefaultStorageManager, shouldSyncToRepo, substitutePatternPlaceholders, summarizeEvaluations, touchCacheEntry, validateCustomTypes, validateMetadata, validateMigratedConfig, validateNameFormat, validateOrg, validateOrganizationName, validatePath, validateRules2 as validatePermissionRules, validateProject, validateRepositoryName, validateRules, validateUri, writeUnifiedConfig };
|
|
7614
|
+
export { AutoSyncPatternSchema, BUILT_IN_TYPES, CODEX_DIRECTORIES, CODEX_URI_PREFIX, CONFIG_SCHEMA_VERSION, CacheManager, CachePersistence, CodexClient, CodexConfigSchema, CodexError, CommonRules, ConfigManager, ConfigurationError, CustomTypeSchema, DEFAULT_CACHE_DIR, DEFAULT_FETCH_OPTIONS, DEFAULT_FRACTARY_GITIGNORE, DEFAULT_GLOBAL_EXCLUDES, DEFAULT_MEMORY_CONFIG, DEFAULT_MIGRATION_OPTIONS, DEFAULT_PERMISSION_CONFIG, DEFAULT_SYNC_CONFIG, DEFAULT_TYPE, FilePluginFileNotFoundError, FilePluginStorage, GitHubStorage, HealthChecker, HttpStorage, LEGACY_PATTERNS, LEGACY_REF_PREFIX, LocalStorage, MEMORY_TYPE_PREFIXES, MemorySearcher, MemoryWriter, MetadataSchema, PERMISSION_LEVEL_ORDER, PermissionDeniedError, PermissionManager, STANDARD_DIRECTORIES, SYNC_PATTERN_PRESETS, StorageManager, SyncManager, SyncRulesSchema, TTL, TypeRegistry, TypesConfigSchema, ValidationError, buildUri, calculateCachePath, calculateContentHash, convertLegacyReference, convertLegacyReferences, convertToUri, createCacheEntry, createCacheManager, createCachePersistence, createCodexClient, createConfigManager, createDefaultRegistry, createEmptyModernConfig, createEmptySyncPlan, createGitHubStorage, createHealthChecker, createHttpStorage, createLocalStorage, createPermissionManager, createRule, createRulesFromPatterns, createStorageManager, createSyncManager, createSyncPlan, deserializeCacheEntry, detectContentType, detectCurrentProject, detectOrganizationFromGit, detectProjectName, detectVersion, discoverCodexRepo, ensureCachePathIgnored, ensureDirectoryStructure, estimateSyncTime, evaluatePath, evaluatePaths, evaluatePatterns, evaluatePermission, evaluatePermissions, expandEnvVars, expandEnvVarsInConfig, extendType, extractEnvVarNames, extractOrgFromRepoName, extractRawFrontmatter, filterByPatterns, filterByPermission, filterPlanOperations, filterSyncablePaths, findLegacyReferences, formatBytes2 as formatBytes, formatDuration, formatPlanSummary, formatSeconds, generateCodexSection, generateMigrationReport, generateReferenceMigrationSummary, generateSyncConfigFromPreset, generateUnifiedConfig, getBuiltInType, getBuiltInTypeNames, getCacheEntryAge, getCacheEntryStatus, getCurrentContext, getCustomSyncDestinations, getDefaultCacheManager, getDefaultConfig, getDefaultDirectories, getDefaultPermissionManager, getDefaultRules, getDefaultStorageManager, getDirectory, getExtension, getFilename, getMigrationRequirements, getPlanStats, getRelativeCachePath, getRemainingTtl, getSyncPreset, getSyncPresetNames, getTargetRepos, hasContentChanged, hasEnvVars, hasFrontmatter, hasLegacyReferences, hasPermission as hasPermissionLevel, installMcpServer, isBuiltInType, isCacheEntryFresh, isCacheEntryValid, isCurrentProjectUri, isLegacyConfig, isLegacyReference, isModernConfig, isAllowed as isPermissionAllowed, isUnifiedConfig, isValidDuration, isValidSize, isValidUri, levelGrants, loadConfig, loadCustomTypes, matchAnyPattern, matchPattern, maxLevel, mergeFetchOptions, mergeRules, mergeTypes, mergeUnifiedConfigs, migrateConfig, migrateFileReferences, minLevel, needsMigration, normalizeCachePath, parseCustomDestination, parseDuration, parseMetadata, parseReference, parseSize, parseTtl, readCodexConfig, readUnifiedConfig, readUnifiedConfig2 as readYamlUnifiedConfig, resolveOrganization, resolveReference, resolveReferences, ruleMatchesAction, ruleMatchesContext, ruleMatchesPath, sanitizeForS3BucketName, sanitizePath, serializeCacheEntry, setDefaultCacheManager, setDefaultPermissionManager, setDefaultStorageManager, shouldSyncToRepo, substitutePatternPlaceholders, summarizeEvaluations, touchCacheEntry, validateCustomTypes, validateMetadata, validateMigratedConfig, validateNameFormat, validateOrg, validateOrganizationName, validatePath, validateRules2 as validatePermissionRules, validateProject, validateRepositoryName, validateRules, validateUri, writeUnifiedConfig };
|
|
7015
7615
|
//# sourceMappingURL=index.js.map
|
|
7016
7616
|
//# sourceMappingURL=index.js.map
|