@bubstack/moe-memory 0.1.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.
- package/.mcp.json +18 -0
- package/README.md +47 -0
- package/agents/search-conversations.md +171 -0
- package/dist/LICENSE +25 -0
- package/dist/cli.d.ts +25 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +125 -0
- package/dist/cli.js.map +1 -0
- package/dist/codex-hook-trust.d.ts +4 -0
- package/dist/codex-hook-trust.d.ts.map +1 -0
- package/dist/codex-hook-trust.js +114 -0
- package/dist/codex-hook-trust.js.map +1 -0
- package/dist/codex-support.d.ts +6 -0
- package/dist/codex-support.d.ts.map +1 -0
- package/dist/codex-support.js +29 -0
- package/dist/codex-support.js.map +1 -0
- package/dist/constants.d.ts +14 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +14 -0
- package/dist/constants.js.map +1 -0
- package/dist/db.d.ts +103 -0
- package/dist/db.d.ts.map +1 -0
- package/dist/db.js +430 -0
- package/dist/db.js.map +1 -0
- package/dist/doctor-cli.d.ts +3 -0
- package/dist/doctor-cli.d.ts.map +1 -0
- package/dist/doctor-cli.js +43 -0
- package/dist/doctor-cli.js.map +1 -0
- package/dist/doctor.d.ts +17 -0
- package/dist/doctor.d.ts.map +1 -0
- package/dist/doctor.js +98 -0
- package/dist/doctor.js.map +1 -0
- package/dist/embedding-migration.d.ts +73 -0
- package/dist/embedding-migration.d.ts.map +1 -0
- package/dist/embedding-migration.js +120 -0
- package/dist/embedding-migration.js.map +1 -0
- package/dist/embeddings.d.ts +78 -0
- package/dist/embeddings.d.ts.map +1 -0
- package/dist/embeddings.js +180 -0
- package/dist/embeddings.js.map +1 -0
- package/dist/file-lock.d.ts +39 -0
- package/dist/file-lock.d.ts.map +1 -0
- package/dist/file-lock.js +91 -0
- package/dist/file-lock.js.map +1 -0
- package/dist/index-cli.d.ts +10 -0
- package/dist/index-cli.d.ts.map +1 -0
- package/dist/index-cli.js +195 -0
- package/dist/index-cli.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/indexer.d.ts +4 -0
- package/dist/indexer.d.ts.map +1 -0
- package/dist/indexer.js +318 -0
- package/dist/indexer.js.map +1 -0
- package/dist/install-check.d.ts +48 -0
- package/dist/install-check.d.ts.map +1 -0
- package/dist/install-check.js +82 -0
- package/dist/install-check.js.map +1 -0
- package/dist/journal/index.d.ts +5 -0
- package/dist/journal/index.d.ts.map +1 -0
- package/dist/journal/index.js +5 -0
- package/dist/journal/index.js.map +1 -0
- package/dist/journal/legacy-sidecars.d.ts +74 -0
- package/dist/journal/legacy-sidecars.d.ts.map +1 -0
- package/dist/journal/legacy-sidecars.js +151 -0
- package/dist/journal/legacy-sidecars.js.map +1 -0
- package/dist/journal/markdown.d.ts +91 -0
- package/dist/journal/markdown.d.ts.map +1 -0
- package/dist/journal/markdown.js +234 -0
- package/dist/journal/markdown.js.map +1 -0
- package/dist/journal/search.d.ts +91 -0
- package/dist/journal/search.d.ts.map +1 -0
- package/dist/journal/search.js +203 -0
- package/dist/journal/search.js.map +1 -0
- package/dist/journal/store.d.ts +96 -0
- package/dist/journal/store.d.ts.map +1 -0
- package/dist/journal/store.js +270 -0
- package/dist/journal/store.js.map +1 -0
- package/dist/journal-cli.d.ts +19 -0
- package/dist/journal-cli.d.ts.map +1 -0
- package/dist/journal-cli.js +153 -0
- package/dist/journal-cli.js.map +1 -0
- package/dist/logging.d.ts +6 -0
- package/dist/logging.d.ts.map +1 -0
- package/dist/logging.js +20 -0
- package/dist/logging.js.map +1 -0
- package/dist/mcp-server.d.ts +34 -0
- package/dist/mcp-server.d.ts.map +1 -0
- package/dist/mcp-server.js +607 -0
- package/dist/mcp-server.js.map +1 -0
- package/dist/parser.d.ts +11 -0
- package/dist/parser.d.ts.map +1 -0
- package/dist/parser.js +445 -0
- package/dist/parser.js.map +1 -0
- package/dist/paths.d.ts +103 -0
- package/dist/paths.d.ts.map +1 -0
- package/dist/paths.js +258 -0
- package/dist/paths.js.map +1 -0
- package/dist/search-cli.d.ts +3 -0
- package/dist/search-cli.d.ts.map +1 -0
- package/dist/search-cli.js +138 -0
- package/dist/search-cli.js.map +1 -0
- package/dist/search.d.ts +36 -0
- package/dist/search.d.ts.map +1 -0
- package/dist/search.js +371 -0
- package/dist/search.js.map +1 -0
- package/dist/show-cli.d.ts +3 -0
- package/dist/show-cli.d.ts.map +1 -0
- package/dist/show-cli.js +60 -0
- package/dist/show-cli.js.map +1 -0
- package/dist/show.d.ts +3 -0
- package/dist/show.d.ts.map +1 -0
- package/dist/show.js +984 -0
- package/dist/show.js.map +1 -0
- package/dist/stats-cli.d.ts +3 -0
- package/dist/stats-cli.d.ts.map +1 -0
- package/dist/stats-cli.js +51 -0
- package/dist/stats-cli.js.map +1 -0
- package/dist/stats.d.ts +19 -0
- package/dist/stats.d.ts.map +1 -0
- package/dist/stats.js +116 -0
- package/dist/stats.js.map +1 -0
- package/dist/summarizer.d.ts +83 -0
- package/dist/summarizer.d.ts.map +1 -0
- package/dist/summarizer.js +526 -0
- package/dist/summarizer.js.map +1 -0
- package/dist/summary-sentinel.d.ts +31 -0
- package/dist/summary-sentinel.d.ts.map +1 -0
- package/dist/summary-sentinel.js +80 -0
- package/dist/summary-sentinel.js.map +1 -0
- package/dist/sync-cli.d.ts +15 -0
- package/dist/sync-cli.d.ts.map +1 -0
- package/dist/sync-cli.js +202 -0
- package/dist/sync-cli.js.map +1 -0
- package/dist/sync.d.ts +35 -0
- package/dist/sync.d.ts.map +1 -0
- package/dist/sync.js +204 -0
- package/dist/sync.js.map +1 -0
- package/dist/types.d.ts +125 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +32 -0
- package/dist/types.js.map +1 -0
- package/dist/verify.d.ts +30 -0
- package/dist/verify.d.ts.map +1 -0
- package/dist/verify.js +174 -0
- package/dist/verify.js.map +1 -0
- package/dist/version.d.ts +13 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +13 -0
- package/dist/version.js.map +1 -0
- package/hooks/hooks.json +15 -0
- package/package.json +71 -0
- package/prompts/search-agent.md +160 -0
- package/skills/remembering-conversations/MCP-TOOLS.md +147 -0
- package/skills/remembering-conversations/SKILL.md +97 -0
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Journal retrieval.
|
|
3
|
+
*
|
|
4
|
+
* Reconciled from private-journal-mcp's `SearchService`. The storage half lost:
|
|
5
|
+
* its `search()` loaded every `.embedding` sidecar under both roots into memory
|
|
6
|
+
* on every query and cosine-scored them in JS — O(n) I/O and O(n) memory per
|
|
7
|
+
* call, with no cache. That is replaced by the same sqlite-vec KNN the
|
|
8
|
+
* conversation half uses, over `vec_journal_entries`.
|
|
9
|
+
*
|
|
10
|
+
* Two things were carried FORWARD unchanged in substance, because they are the
|
|
11
|
+
* best-engineered code in that source and the conversation half has no
|
|
12
|
+
* equivalent:
|
|
13
|
+
*
|
|
14
|
+
* - `readEntry`'s two-stage containment guard: resolve, require `.md`, require
|
|
15
|
+
* containment in the journal roots, realpath, then require containment in
|
|
16
|
+
* the REALPATH'd roots. The second stage blocks a symlink escape, and the
|
|
17
|
+
* two stages exist precisely because macOS `/tmp` and `/var` are symlinks —
|
|
18
|
+
* do not "simplify" it to one check.
|
|
19
|
+
* - `generateExcerpt`, in ./markdown.ts.
|
|
20
|
+
*
|
|
21
|
+
* `SearchResult` and `SearchOptions` were exported by BOTH upstream `search.ts`
|
|
22
|
+
* files with different shapes. The conversation names keep their meaning; these
|
|
23
|
+
* are `JournalSearchResult` and `JournalSearchOptions`, so the barrel can export
|
|
24
|
+
* all four.
|
|
25
|
+
*/
|
|
26
|
+
import fs from "node:fs/promises";
|
|
27
|
+
import path from "node:path";
|
|
28
|
+
import { JOURNAL_SELECT_COLUMNS, journalEntryFromRow } from "../db.js";
|
|
29
|
+
import { generateQueryEmbedding } from "../embeddings.js";
|
|
30
|
+
import { l2DistanceToCosineSimilarity } from "../search.js";
|
|
31
|
+
import { generateExcerpt, sectionsMatch } from "./markdown.js";
|
|
32
|
+
function buildFilters(options, roots) {
|
|
33
|
+
const parts = [];
|
|
34
|
+
const params = [];
|
|
35
|
+
// Confine retrieval to the roots this service was constructed with.
|
|
36
|
+
//
|
|
37
|
+
// The service has always been handed them and never used them for filtering,
|
|
38
|
+
// so a `scope: "project"` entry written in one repo was returned in every
|
|
39
|
+
// other repo on the machine — they all share one database. The roots are the
|
|
40
|
+
// current project's journal directory plus the shared user directory, so user
|
|
41
|
+
// entries, which deliberately follow the person between projects, still match.
|
|
42
|
+
if (roots.length > 0) {
|
|
43
|
+
parts.push(`j.root IN (${roots.map(() => "?").join(", ")})`);
|
|
44
|
+
params.push(...roots);
|
|
45
|
+
}
|
|
46
|
+
if (options.scope && options.scope !== "both") {
|
|
47
|
+
parts.push("j.scope = ?");
|
|
48
|
+
params.push(options.scope);
|
|
49
|
+
}
|
|
50
|
+
if (options.dateRange?.start) {
|
|
51
|
+
parts.push("j.timestamp >= ?");
|
|
52
|
+
params.push(options.dateRange.start.getTime());
|
|
53
|
+
}
|
|
54
|
+
if (options.dateRange?.end) {
|
|
55
|
+
parts.push("j.timestamp <= ?");
|
|
56
|
+
params.push(options.dateRange.end.getTime());
|
|
57
|
+
}
|
|
58
|
+
return { sql: parts.length ? `AND ${parts.join(" AND ")}` : "", params };
|
|
59
|
+
}
|
|
60
|
+
export class JournalSearchService {
|
|
61
|
+
db;
|
|
62
|
+
roots;
|
|
63
|
+
embedQuery;
|
|
64
|
+
/**
|
|
65
|
+
* @param db an open database from `initDatabase()`
|
|
66
|
+
* @param roots the journal roots, already de-duplicated (see `JournalStore.roots`)
|
|
67
|
+
*/
|
|
68
|
+
constructor(db, roots, options = {}) {
|
|
69
|
+
this.db = db;
|
|
70
|
+
this.roots = roots.map((root) => path.resolve(root));
|
|
71
|
+
this.embedQuery = options.embedQuery ?? generateQueryEmbedding;
|
|
72
|
+
}
|
|
73
|
+
async search(query, options = {}) {
|
|
74
|
+
const limit = options.limit ?? 10;
|
|
75
|
+
const minScore = options.minScore ?? 0.1;
|
|
76
|
+
const sections = options.sections ?? [];
|
|
77
|
+
const queryEmbedding = await this.embedQuery(query);
|
|
78
|
+
const { sql: filterClause, params: filterParams } = buildFilters(options, this.roots);
|
|
79
|
+
// vec0 applies KNN before WHERE, so ask for more candidates than `limit`
|
|
80
|
+
// whenever a filter is active and trim afterwards — the same correction the
|
|
81
|
+
// conversation search makes for its metadata filters.
|
|
82
|
+
const overfetch = filterClause || sections.length > 0 ? 5 : 1;
|
|
83
|
+
const rows = this.db
|
|
84
|
+
.prepare(`
|
|
85
|
+
SELECT
|
|
86
|
+
${JOURNAL_SELECT_COLUMNS},
|
|
87
|
+
vec.distance
|
|
88
|
+
FROM vec_journal_entries AS vec
|
|
89
|
+
JOIN journal_entries AS j ON vec.id = j.id
|
|
90
|
+
WHERE vec.embedding MATCH ?
|
|
91
|
+
AND k = ?
|
|
92
|
+
${filterClause}
|
|
93
|
+
ORDER BY vec.distance ASC
|
|
94
|
+
`)
|
|
95
|
+
.all(Buffer.from(new Float32Array(queryEmbedding).buffer), limit * overfetch, ...filterParams);
|
|
96
|
+
const results = [];
|
|
97
|
+
for (const row of rows) {
|
|
98
|
+
const entry = journalEntryFromRow(row);
|
|
99
|
+
if (!sectionsMatch(entry.sections, sections))
|
|
100
|
+
continue;
|
|
101
|
+
const score = l2DistanceToCosineSimilarity(row.distance);
|
|
102
|
+
if (score < minScore)
|
|
103
|
+
continue;
|
|
104
|
+
results.push({ entry, score, excerpt: generateExcerpt(entry.text, query) });
|
|
105
|
+
if (results.length >= limit)
|
|
106
|
+
break;
|
|
107
|
+
}
|
|
108
|
+
return results;
|
|
109
|
+
}
|
|
110
|
+
listRecent(options = {}) {
|
|
111
|
+
const limit = options.limit ?? 10;
|
|
112
|
+
const { sql: filterClause, params: filterParams } = buildFilters(options, this.roots);
|
|
113
|
+
const rows = this.db
|
|
114
|
+
.prepare(`
|
|
115
|
+
SELECT ${JOURNAL_SELECT_COLUMNS}
|
|
116
|
+
FROM journal_entries AS j
|
|
117
|
+
WHERE 1 = 1
|
|
118
|
+
${filterClause}
|
|
119
|
+
ORDER BY j.timestamp DESC
|
|
120
|
+
LIMIT ?
|
|
121
|
+
`)
|
|
122
|
+
.all(...filterParams, limit);
|
|
123
|
+
return rows.map((row) => {
|
|
124
|
+
const entry = journalEntryFromRow(row);
|
|
125
|
+
// No similarity score for a chronological listing — upstream reported 1.
|
|
126
|
+
return { entry, score: 1, excerpt: generateExcerpt(entry.text, "", 150) };
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
async readRecentEntries(options = {}) {
|
|
130
|
+
const recent = this.listRecent({ limit: options.limit ?? 5, scope: options.scope });
|
|
131
|
+
const results = [];
|
|
132
|
+
for (const hit of recent) {
|
|
133
|
+
const content = await this.readEntry(hit.entry.path);
|
|
134
|
+
if (content !== null) {
|
|
135
|
+
results.push({
|
|
136
|
+
path: hit.entry.path,
|
|
137
|
+
content,
|
|
138
|
+
sections: hit.entry.sections,
|
|
139
|
+
timestamp: hit.entry.timestamp,
|
|
140
|
+
scope: hit.entry.scope,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return results;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Read one entry off disk.
|
|
148
|
+
*
|
|
149
|
+
* Carried over from private-journal-mcp with its guard intact. The error text
|
|
150
|
+
* is treated as API by three assertions, so it is preserved verbatim.
|
|
151
|
+
*/
|
|
152
|
+
async readEntry(filePath) {
|
|
153
|
+
const resolvedPath = path.resolve(filePath);
|
|
154
|
+
// Only Markdown entries under the journal directories are readable; reject
|
|
155
|
+
// anything else with a clear error.
|
|
156
|
+
if (path.extname(resolvedPath).toLowerCase() !== ".md") {
|
|
157
|
+
throw new Error(`${resolvedPath} is not a readable journal entry`);
|
|
158
|
+
}
|
|
159
|
+
if (!isUnderRoot(resolvedPath, this.roots)) {
|
|
160
|
+
throw new Error(`${resolvedPath} is not a readable journal entry`);
|
|
161
|
+
}
|
|
162
|
+
let realPath;
|
|
163
|
+
try {
|
|
164
|
+
realPath = await fs.realpath(resolvedPath);
|
|
165
|
+
}
|
|
166
|
+
catch (error) {
|
|
167
|
+
if (error?.code === "ENOENT") {
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
throw error;
|
|
171
|
+
}
|
|
172
|
+
// Resolve symlinks and confirm the real file is still under a journal
|
|
173
|
+
// directory before reading.
|
|
174
|
+
if (!isUnderRoot(realPath, await this.realRoots())) {
|
|
175
|
+
throw new Error(`${resolvedPath} is not a readable journal entry`);
|
|
176
|
+
}
|
|
177
|
+
try {
|
|
178
|
+
return await fs.readFile(realPath, "utf8");
|
|
179
|
+
}
|
|
180
|
+
catch (error) {
|
|
181
|
+
if (error?.code === "ENOENT") {
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
throw error;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
async realRoots() {
|
|
188
|
+
const roots = [];
|
|
189
|
+
for (const root of this.roots) {
|
|
190
|
+
try {
|
|
191
|
+
roots.push(await fs.realpath(root));
|
|
192
|
+
}
|
|
193
|
+
catch {
|
|
194
|
+
// A journal root that doesn't exist on disk can't contain anything.
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return roots;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
function isUnderRoot(candidate, roots) {
|
|
201
|
+
return roots.some((root) => candidate === root || candidate.startsWith(root + path.sep));
|
|
202
|
+
}
|
|
203
|
+
//# sourceMappingURL=search.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.js","sourceRoot":"","sources":["../../src/journal/search.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The journal write path and its index.
|
|
3
|
+
*
|
|
4
|
+
* Reconciled from private-journal-mcp's `JournalManager`. The markdown-writing
|
|
5
|
+
* half is carried over essentially unchanged — it is a data contract with files
|
|
6
|
+
* already on disk. The persistence half changed completely: entries are indexed
|
|
7
|
+
* into the shared SQLite store instead of a `.embedding` JSON sidecar per file.
|
|
8
|
+
*
|
|
9
|
+
* That swap fixes a real defect. Upstream, `generateEmbeddingForEntry` caught and
|
|
10
|
+
* logged every embedding error so "embedding failure shouldn't prevent journal
|
|
11
|
+
* writing" — but the sidecar index was the ONLY enumeration path in the package,
|
|
12
|
+
* so a failed encode wrote an entry that no read path could ever see again, with
|
|
13
|
+
* no doctor or verify command to notice. Here the markdown files are the source
|
|
14
|
+
* of truth: `indexJournal()` walks them, and anything missing from the index or
|
|
15
|
+
* behind the current EMBEDDING_VERSION is picked up on the next run.
|
|
16
|
+
*/
|
|
17
|
+
import type Database from "better-sqlite3";
|
|
18
|
+
import { type EmbedFn } from "../embeddings.js";
|
|
19
|
+
import type { JournalScope, JournalThoughts } from "../types.js";
|
|
20
|
+
export interface JournalStoreOptions {
|
|
21
|
+
projectPath?: string | undefined;
|
|
22
|
+
userPath?: string | undefined;
|
|
23
|
+
/**
|
|
24
|
+
* Injected encoder. Defaults to the real bge pipeline; the offline test
|
|
25
|
+
* project passes a deterministic stub so the write path and the index can be
|
|
26
|
+
* exercised without a 35 MB model download.
|
|
27
|
+
*/
|
|
28
|
+
embed?: EmbedFn | undefined;
|
|
29
|
+
}
|
|
30
|
+
export interface JournalIndexResult {
|
|
31
|
+
indexed: number;
|
|
32
|
+
pruned: number;
|
|
33
|
+
failed: number;
|
|
34
|
+
total: number;
|
|
35
|
+
}
|
|
36
|
+
interface JournalRoot {
|
|
37
|
+
scope: JournalScope;
|
|
38
|
+
path: string;
|
|
39
|
+
}
|
|
40
|
+
export declare class JournalStore {
|
|
41
|
+
private readonly projectPath;
|
|
42
|
+
private readonly userPath;
|
|
43
|
+
private readonly embed;
|
|
44
|
+
constructor(options?: JournalStoreOptions);
|
|
45
|
+
/**
|
|
46
|
+
* The roots to walk, de-duplicated.
|
|
47
|
+
*
|
|
48
|
+
* When `MOE_MEMORY_JOURNAL_PATH` is set both resolve to the same directory.
|
|
49
|
+
* Upstream loaded such a directory twice — once labelled `project`, once
|
|
50
|
+
* `user` — so every entry appeared twice with contradictory labels and
|
|
51
|
+
* `limit: 10` yielded 5 unique entries. That is the documented containerised
|
|
52
|
+
* configuration, i.e. the one most likely to be used in infra.
|
|
53
|
+
*/
|
|
54
|
+
roots(): JournalRoot[];
|
|
55
|
+
private get collapsed();
|
|
56
|
+
/**
|
|
57
|
+
* Which journal an entry belongs to.
|
|
58
|
+
*
|
|
59
|
+
* Normally the directory decides. When the roots are collapsed there is no
|
|
60
|
+
* directory to decide, so the entry's own sections do: `writeThoughts` routes
|
|
61
|
+
* `project_notes` to the project journal and nothing else, so the presence of
|
|
62
|
+
* a `## Project Notes` heading is a faithful discriminator either way.
|
|
63
|
+
*/
|
|
64
|
+
private scopeFor;
|
|
65
|
+
/**
|
|
66
|
+
* Write one set of thoughts. `project_notes` lands in the project journal, the
|
|
67
|
+
* other five in the user journal, and each side is skipped when empty — so a
|
|
68
|
+
* `process_thoughts` call carrying only reflections creates no project
|
|
69
|
+
* directory at all.
|
|
70
|
+
*
|
|
71
|
+
* `db` is optional: pass it to index the new entries immediately (what the MCP
|
|
72
|
+
* server does), omit it to write markdown only.
|
|
73
|
+
*/
|
|
74
|
+
writeThoughts(thoughts: JournalThoughts, db?: Database.Database): Promise<string[]>;
|
|
75
|
+
private writeToLocation;
|
|
76
|
+
private ensureDirectoryExists;
|
|
77
|
+
/**
|
|
78
|
+
* Index one markdown file by absolute path. Used straight after a write.
|
|
79
|
+
*/
|
|
80
|
+
private indexOne;
|
|
81
|
+
private indexContent;
|
|
82
|
+
/**
|
|
83
|
+
* Walk both journal roots and bring the index up to date.
|
|
84
|
+
*
|
|
85
|
+
* Replaces private-journal-mcp's `generateMissingEmbeddings()`, which keyed
|
|
86
|
+
* purely on the absence of a `.embedding` sidecar. This one re-indexes when
|
|
87
|
+
* the file's mtime moved or its row is behind EMBEDDING_VERSION, and prunes
|
|
88
|
+
* rows whose file is gone — so an edited entry, a bumped encoder and a deleted
|
|
89
|
+
* file are all handled.
|
|
90
|
+
*/
|
|
91
|
+
indexJournal(db: Database.Database): Promise<JournalIndexResult>;
|
|
92
|
+
/** Count indexed entries, optionally for one scope. */
|
|
93
|
+
count(db: Database.Database, scope?: JournalScope): number;
|
|
94
|
+
}
|
|
95
|
+
export {};
|
|
96
|
+
//# sourceMappingURL=store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/journal/store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAIH,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAQ3C,OAAO,EAAE,KAAK,OAAO,EAA0B,MAAM,kBAAkB,CAAC;AAExE,OAAO,KAAK,EAAgB,YAAY,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAY/E,MAAM,WAAW,mBAAmB;IAClC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B;;;;OAIG;IACH,KAAK,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CAC7B;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACf;AAED,UAAU,WAAW;IACnB,KAAK,EAAE,YAAY,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;CACd;AA4BD,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAU;gBAEpB,OAAO,GAAE,mBAAwB;IAM7C;;;;;;;;OAQG;IACH,KAAK,IAAI,WAAW,EAAE;IAUtB,OAAO,KAAK,SAAS,GAEpB;IAED;;;;;;;OAOG;IACH,OAAO,CAAC,QAAQ;IAQhB;;;;;;;;OAQG;IACG,aAAa,CAAC,QAAQ,EAAE,eAAe,EAAE,EAAE,CAAC,EAAE,QAAQ,CAAC,QAAQ,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;YA+B3E,eAAe;YAaf,qBAAqB;IAgBnC;;OAEG;YACW,QAAQ;YAYR,YAAY;IA8B1B;;;;;;;;OAQG;IACG,YAAY,CAAC,EAAE,EAAE,QAAQ,CAAC,QAAQ,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAuFtE,uDAAuD;IACvD,KAAK,CAAC,EAAE,EAAE,QAAQ,CAAC,QAAQ,EAAE,KAAK,CAAC,EAAE,YAAY,GAAG,MAAM;CAG3D"}
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The journal write path and its index.
|
|
3
|
+
*
|
|
4
|
+
* Reconciled from private-journal-mcp's `JournalManager`. The markdown-writing
|
|
5
|
+
* half is carried over essentially unchanged — it is a data contract with files
|
|
6
|
+
* already on disk. The persistence half changed completely: entries are indexed
|
|
7
|
+
* into the shared SQLite store instead of a `.embedding` JSON sidecar per file.
|
|
8
|
+
*
|
|
9
|
+
* That swap fixes a real defect. Upstream, `generateEmbeddingForEntry` caught and
|
|
10
|
+
* logged every embedding error so "embedding failure shouldn't prevent journal
|
|
11
|
+
* writing" — but the sidecar index was the ONLY enumeration path in the package,
|
|
12
|
+
* so a failed encode wrote an entry that no read path could ever see again, with
|
|
13
|
+
* no doctor or verify command to notice. Here the markdown files are the source
|
|
14
|
+
* of truth: `indexJournal()` walks them, and anything missing from the index or
|
|
15
|
+
* behind the current EMBEDDING_VERSION is picked up on the next run.
|
|
16
|
+
*/
|
|
17
|
+
import fs from "node:fs/promises";
|
|
18
|
+
import path from "node:path";
|
|
19
|
+
import { countJournalEntries, deleteJournalEntry, getJournalIndexState, upsertJournalEntry, } from "../db.js";
|
|
20
|
+
import { EMBEDDING_VERSION } from "../embedding-migration.js";
|
|
21
|
+
import { generateEntryEmbedding } from "../embeddings.js";
|
|
22
|
+
import { resolveProjectJournalPath, resolveUserJournalPath } from "../paths.js";
|
|
23
|
+
import { DAY_DIR_PATTERN, extractSearchableText, formatDayDirectory, formatEntry, formatEntryBasename, journalEntryId, timestampFromEntryPath, timestampFromFrontmatter, } from "./markdown.js";
|
|
24
|
+
/**
|
|
25
|
+
* `project_notes` is the only category that belongs to the codebase. The other
|
|
26
|
+
* five belong to the person, and follow them between projects.
|
|
27
|
+
*/
|
|
28
|
+
const PROJECT_KEYS = ["project_notes"];
|
|
29
|
+
const USER_KEYS = [
|
|
30
|
+
"reflections",
|
|
31
|
+
"observations",
|
|
32
|
+
"user_context",
|
|
33
|
+
"technical_insights",
|
|
34
|
+
"world_knowledge",
|
|
35
|
+
];
|
|
36
|
+
function pick(thoughts, keys) {
|
|
37
|
+
const out = {};
|
|
38
|
+
for (const key of keys) {
|
|
39
|
+
const value = thoughts[key];
|
|
40
|
+
if (value !== undefined)
|
|
41
|
+
out[key] = value;
|
|
42
|
+
}
|
|
43
|
+
return out;
|
|
44
|
+
}
|
|
45
|
+
function hasContent(thoughts) {
|
|
46
|
+
return Object.values(thoughts).some((value) => value !== undefined && value !== "");
|
|
47
|
+
}
|
|
48
|
+
export class JournalStore {
|
|
49
|
+
projectPath;
|
|
50
|
+
userPath;
|
|
51
|
+
embed;
|
|
52
|
+
constructor(options = {}) {
|
|
53
|
+
this.projectPath = path.resolve(options.projectPath ?? resolveProjectJournalPath());
|
|
54
|
+
this.userPath = path.resolve(options.userPath ?? resolveUserJournalPath());
|
|
55
|
+
this.embed = options.embed ?? generateEntryEmbedding;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* The roots to walk, de-duplicated.
|
|
59
|
+
*
|
|
60
|
+
* When `MOE_MEMORY_JOURNAL_PATH` is set both resolve to the same directory.
|
|
61
|
+
* Upstream loaded such a directory twice — once labelled `project`, once
|
|
62
|
+
* `user` — so every entry appeared twice with contradictory labels and
|
|
63
|
+
* `limit: 10` yielded 5 unique entries. That is the documented containerised
|
|
64
|
+
* configuration, i.e. the one most likely to be used in infra.
|
|
65
|
+
*/
|
|
66
|
+
roots() {
|
|
67
|
+
if (this.projectPath === this.userPath) {
|
|
68
|
+
return [{ scope: "user", path: this.userPath }];
|
|
69
|
+
}
|
|
70
|
+
return [
|
|
71
|
+
{ scope: "project", path: this.projectPath },
|
|
72
|
+
{ scope: "user", path: this.userPath },
|
|
73
|
+
];
|
|
74
|
+
}
|
|
75
|
+
get collapsed() {
|
|
76
|
+
return this.projectPath === this.userPath;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Which journal an entry belongs to.
|
|
80
|
+
*
|
|
81
|
+
* Normally the directory decides. When the roots are collapsed there is no
|
|
82
|
+
* directory to decide, so the entry's own sections do: `writeThoughts` routes
|
|
83
|
+
* `project_notes` to the project journal and nothing else, so the presence of
|
|
84
|
+
* a `## Project Notes` heading is a faithful discriminator either way.
|
|
85
|
+
*/
|
|
86
|
+
scopeFor(root, sections) {
|
|
87
|
+
if (!this.collapsed)
|
|
88
|
+
return root.scope;
|
|
89
|
+
const isProject = sections.some((section) => section.toLowerCase().replace(/[^a-z0-9]/g, "") === "projectnotes");
|
|
90
|
+
return isProject ? "project" : "user";
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Write one set of thoughts. `project_notes` lands in the project journal, the
|
|
94
|
+
* other five in the user journal, and each side is skipped when empty — so a
|
|
95
|
+
* `process_thoughts` call carrying only reflections creates no project
|
|
96
|
+
* directory at all.
|
|
97
|
+
*
|
|
98
|
+
* `db` is optional: pass it to index the new entries immediately (what the MCP
|
|
99
|
+
* server does), omit it to write markdown only.
|
|
100
|
+
*/
|
|
101
|
+
async writeThoughts(thoughts, db) {
|
|
102
|
+
const timestamp = new Date();
|
|
103
|
+
const written = [];
|
|
104
|
+
const projectThoughts = pick(thoughts, PROJECT_KEYS);
|
|
105
|
+
if (hasContent(projectThoughts)) {
|
|
106
|
+
written.push(await this.writeToLocation(projectThoughts, timestamp, this.projectPath));
|
|
107
|
+
}
|
|
108
|
+
const userThoughts = pick(thoughts, USER_KEYS);
|
|
109
|
+
if (hasContent(userThoughts)) {
|
|
110
|
+
written.push(await this.writeToLocation(userThoughts, timestamp, this.userPath));
|
|
111
|
+
}
|
|
112
|
+
if (db) {
|
|
113
|
+
for (const entryPath of written) {
|
|
114
|
+
await this.indexOne(db, entryPath).catch((error) => {
|
|
115
|
+
// Not fatal, and no longer invisible: the markdown file is the source
|
|
116
|
+
// of truth and indexJournal() will retry this entry on the next run.
|
|
117
|
+
console.error(`moe-memory: journal entry written but not indexed (${entryPath}): ${error instanceof Error ? error.message : String(error)}`);
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return written;
|
|
122
|
+
}
|
|
123
|
+
async writeToLocation(thoughts, timestamp, basePath) {
|
|
124
|
+
const dayDirectory = path.join(basePath, formatDayDirectory(timestamp));
|
|
125
|
+
const filePath = path.join(dayDirectory, `${formatEntryBasename(timestamp)}.md`);
|
|
126
|
+
await this.ensureDirectoryExists(dayDirectory);
|
|
127
|
+
await fs.writeFile(filePath, formatEntry(thoughts, timestamp), "utf8");
|
|
128
|
+
return filePath;
|
|
129
|
+
}
|
|
130
|
+
async ensureDirectoryExists(dirPath) {
|
|
131
|
+
try {
|
|
132
|
+
await fs.access(dirPath);
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
try {
|
|
136
|
+
await fs.mkdir(dirPath, { recursive: true });
|
|
137
|
+
}
|
|
138
|
+
catch (mkdirError) {
|
|
139
|
+
throw new Error(`Failed to create journal directory at ${dirPath}: ${mkdirError instanceof Error ? mkdirError.message : String(mkdirError)}`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Index one markdown file by absolute path. Used straight after a write.
|
|
145
|
+
*/
|
|
146
|
+
async indexOne(db, entryPath) {
|
|
147
|
+
const root = this.roots().find((candidate) => entryPath === candidate.path || entryPath.startsWith(candidate.path + path.sep));
|
|
148
|
+
if (!root)
|
|
149
|
+
throw new Error(`${entryPath} is not under a journal root`);
|
|
150
|
+
const content = await fs.readFile(entryPath, "utf8");
|
|
151
|
+
const stat = await fs.stat(entryPath);
|
|
152
|
+
await this.indexContent(db, root, entryPath, content, stat.mtimeMs);
|
|
153
|
+
}
|
|
154
|
+
async indexContent(db, root, entryPath, content, mtimeMs) {
|
|
155
|
+
const { text, sections } = extractSearchableText(content);
|
|
156
|
+
if (text.trim().length === 0)
|
|
157
|
+
return; // Nothing to embed
|
|
158
|
+
const scope = this.scopeFor(root, sections);
|
|
159
|
+
// Frontmatter first (epoch ms), then the filename (second resolution), then
|
|
160
|
+
// the file's mtime. See timestampFromFrontmatter for why the order matters.
|
|
161
|
+
const timestamp = timestampFromFrontmatter(content) ?? timestampFromEntryPath(entryPath)?.getTime() ?? mtimeMs;
|
|
162
|
+
const entry = {
|
|
163
|
+
id: journalEntryId(scope, root.path, entryPath),
|
|
164
|
+
path: entryPath,
|
|
165
|
+
root: path.resolve(root.path),
|
|
166
|
+
scope,
|
|
167
|
+
timestamp,
|
|
168
|
+
text,
|
|
169
|
+
sections,
|
|
170
|
+
};
|
|
171
|
+
const embedding = await this.embed(text);
|
|
172
|
+
upsertJournalEntry(db, entry, mtimeMs, embedding);
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Walk both journal roots and bring the index up to date.
|
|
176
|
+
*
|
|
177
|
+
* Replaces private-journal-mcp's `generateMissingEmbeddings()`, which keyed
|
|
178
|
+
* purely on the absence of a `.embedding` sidecar. This one re-indexes when
|
|
179
|
+
* the file's mtime moved or its row is behind EMBEDDING_VERSION, and prunes
|
|
180
|
+
* rows whose file is gone — so an edited entry, a bumped encoder and a deleted
|
|
181
|
+
* file are all handled.
|
|
182
|
+
*/
|
|
183
|
+
async indexJournal(db) {
|
|
184
|
+
const result = { indexed: 0, pruned: 0, failed: 0, total: 0 };
|
|
185
|
+
const state = getJournalIndexState(db);
|
|
186
|
+
const seen = new Set();
|
|
187
|
+
for (const root of this.roots()) {
|
|
188
|
+
let dayDirs;
|
|
189
|
+
try {
|
|
190
|
+
dayDirs = await fs.readdir(root.path);
|
|
191
|
+
}
|
|
192
|
+
catch (error) {
|
|
193
|
+
if (error?.code !== "ENOENT") {
|
|
194
|
+
console.error(`moe-memory: failed to scan journal root ${root.path}: ${String(error)}`);
|
|
195
|
+
}
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
for (const dayDir of dayDirs) {
|
|
199
|
+
if (!DAY_DIR_PATTERN.test(dayDir))
|
|
200
|
+
continue;
|
|
201
|
+
const dayPath = path.join(root.path, dayDir);
|
|
202
|
+
let files;
|
|
203
|
+
try {
|
|
204
|
+
const stat = await fs.stat(dayPath);
|
|
205
|
+
if (!stat.isDirectory())
|
|
206
|
+
continue;
|
|
207
|
+
files = await fs.readdir(dayPath);
|
|
208
|
+
}
|
|
209
|
+
catch {
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
212
|
+
for (const file of files) {
|
|
213
|
+
if (!file.endsWith(".md"))
|
|
214
|
+
continue;
|
|
215
|
+
const entryPath = path.join(dayPath, file);
|
|
216
|
+
result.total++;
|
|
217
|
+
try {
|
|
218
|
+
const content = await fs.readFile(entryPath, "utf8");
|
|
219
|
+
const stat = await fs.stat(entryPath);
|
|
220
|
+
const { sections } = extractSearchableText(content);
|
|
221
|
+
const scope = this.scopeFor(root, sections);
|
|
222
|
+
const id = journalEntryId(scope, root.path, entryPath);
|
|
223
|
+
seen.add(id);
|
|
224
|
+
const existing = state.get(id);
|
|
225
|
+
const fresh = existing !== undefined &&
|
|
226
|
+
existing.path === entryPath &&
|
|
227
|
+
existing.sourceMtimeMs === stat.mtimeMs &&
|
|
228
|
+
existing.embeddingVersion === EMBEDDING_VERSION;
|
|
229
|
+
if (fresh)
|
|
230
|
+
continue;
|
|
231
|
+
await this.indexContent(db, root, entryPath, content, stat.mtimeMs);
|
|
232
|
+
result.indexed++;
|
|
233
|
+
}
|
|
234
|
+
catch (error) {
|
|
235
|
+
result.failed++;
|
|
236
|
+
console.error(`moe-memory: failed to index journal entry ${entryPath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
// Prune only what this walk was actually responsible for.
|
|
242
|
+
//
|
|
243
|
+
// One database holds every project's entries, and `seen` can only ever
|
|
244
|
+
// contain ids from the roots above. Deleting everything absent from it
|
|
245
|
+
// therefore deleted other repos' project notes — permanently, on every
|
|
246
|
+
// server start. A row is a candidate for pruning only if its root is one of
|
|
247
|
+
// the roots just walked; anything else belongs to a project that is not this
|
|
248
|
+
// one, and its own next index is what will prune it.
|
|
249
|
+
//
|
|
250
|
+
// Rows written before `journal_entries.root` existed carry `''` and are
|
|
251
|
+
// never pruned here. migrateJournalRoot clears them once, so the case is
|
|
252
|
+
// transitional; treating an unknown root as "not mine" is the safe default
|
|
253
|
+
// regardless.
|
|
254
|
+
const walked = new Set(this.roots().map((root) => path.resolve(root.path)));
|
|
255
|
+
for (const [id, row] of state) {
|
|
256
|
+
if (seen.has(id))
|
|
257
|
+
continue;
|
|
258
|
+
if (!row.root || !walked.has(row.root))
|
|
259
|
+
continue;
|
|
260
|
+
deleteJournalEntry(db, id);
|
|
261
|
+
result.pruned++;
|
|
262
|
+
}
|
|
263
|
+
return result;
|
|
264
|
+
}
|
|
265
|
+
/** Count indexed entries, optionally for one scope. */
|
|
266
|
+
count(db, scope) {
|
|
267
|
+
return countJournalEntries(db, scope);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
//# sourceMappingURL=store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/journal/store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `moe-memory journal <subcommand>` — the journal half from the terminal.
|
|
3
|
+
*
|
|
4
|
+
* private-journal-mcp had no CLI beyond `--journal-path`: its single bin was the
|
|
5
|
+
* MCP server and nothing else. These subcommands are new surface, and they exist
|
|
6
|
+
* because the journal is no longer self-indexing through sidecars — if the index
|
|
7
|
+
* can be rebuilt, something has to be able to rebuild it.
|
|
8
|
+
*/
|
|
9
|
+
import type { JournalScopeFilter } from "./journal/search.js";
|
|
10
|
+
export interface JournalArgs {
|
|
11
|
+
/** Positional arguments joined — the search query. */
|
|
12
|
+
query: string;
|
|
13
|
+
limit: number;
|
|
14
|
+
scope: JournalScopeFilter;
|
|
15
|
+
journalPath: string | undefined;
|
|
16
|
+
}
|
|
17
|
+
export declare function parseJournalArgs(args: string[]): JournalArgs;
|
|
18
|
+
export declare function runJournal(args: string[]): Promise<number>;
|
|
19
|
+
//# sourceMappingURL=journal-cli.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"journal-cli.d.ts","sourceRoot":"","sources":["../src/journal-cli.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AA6C9D,MAAM,WAAW,WAAW;IAC1B,sDAAsD;IACtD,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,kBAAkB,CAAC;IAC1B,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;CACjC;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,WAAW,CAiC5D;AAED,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAwFhE"}
|