@s_s/mnemo 1.0.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/README.md +193 -0
- package/build/core/config.d.ts +54 -0
- package/build/core/config.js +61 -0
- package/build/core/config.js.map +1 -0
- package/build/core/embedding.d.ts +37 -0
- package/build/core/embedding.js +119 -0
- package/build/core/embedding.js.map +1 -0
- package/build/core/notes.d.ts +36 -0
- package/build/core/notes.js +187 -0
- package/build/core/notes.js.map +1 -0
- package/build/index.d.ts +2 -0
- package/build/index.js +31 -0
- package/build/index.js.map +1 -0
- package/build/prompts/templates.d.ts +21 -0
- package/build/prompts/templates.js +102 -0
- package/build/prompts/templates.js.map +1 -0
- package/build/tools/compress.d.ts +5 -0
- package/build/tools/compress.js +197 -0
- package/build/tools/compress.js.map +1 -0
- package/build/tools/save.d.ts +5 -0
- package/build/tools/save.js +67 -0
- package/build/tools/save.js.map +1 -0
- package/build/tools/search.d.ts +5 -0
- package/build/tools/search.js +107 -0
- package/build/tools/search.js.map +1 -0
- package/build/tools/setup.d.ts +5 -0
- package/build/tools/setup.js +120 -0
- package/build/tools/setup.js.map +1 -0
- package/package.json +49 -0
package/README.md
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# Mnemo
|
|
2
|
+
|
|
3
|
+
[中文文档](./docs/README.zh-CN.md)
|
|
4
|
+
|
|
5
|
+
Persistent memory management for AI coding assistants via [MCP](https://modelcontextprotocol.io/).
|
|
6
|
+
|
|
7
|
+
Mnemo solves the problem of context window overflow — important decisions, user preferences, and project knowledge get lost when conversations reset. Mnemo distills key information into persistent memory notes that can be recalled across sessions using semantic search.
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- **Semantic search** — find memories by meaning, not just keywords (powered by local embeddings)
|
|
12
|
+
- **Multi-agent support** — works with OpenCode, Claude Code, Openclaw, and Codex
|
|
13
|
+
- **Fully local** — no API calls, no cloud storage; all data stays on your machine
|
|
14
|
+
- **Auto-prompted** — injects instructions into your agent's config so it knows when to save and recall memories
|
|
15
|
+
- **Compression workflow** — atomic distillation of old notes into fewer, concise ones
|
|
16
|
+
|
|
17
|
+
## Quick Start
|
|
18
|
+
|
|
19
|
+
### Install
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm install -g mnemo
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### Configure your MCP client
|
|
26
|
+
|
|
27
|
+
Add Mnemo to your MCP client configuration. For example, in OpenCode (`opencode.json`):
|
|
28
|
+
|
|
29
|
+
```json
|
|
30
|
+
{
|
|
31
|
+
"mcp": {
|
|
32
|
+
"mnemo": {
|
|
33
|
+
"command": "mnemo"
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
For Claude Code (`.claude/settings.json`):
|
|
40
|
+
|
|
41
|
+
```json
|
|
42
|
+
{
|
|
43
|
+
"mcpServers": {
|
|
44
|
+
"mnemo": {
|
|
45
|
+
"command": "mnemo"
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Initialize
|
|
52
|
+
|
|
53
|
+
Once connected, call the `memory_setup` tool to inject memory management instructions into your agent's config file:
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
> Use the memory_setup tool to initialize Mnemo
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
This writes a prompt block into your agent's config (e.g., `AGENTS.md` for OpenCode, `CLAUDE.md` for Claude Code) that teaches the agent when and how to use Mnemo's tools.
|
|
60
|
+
|
|
61
|
+
## Usage Examples
|
|
62
|
+
|
|
63
|
+
> **Important:** You don't call Mnemo tools directly. You chat with your AI agent in natural language, and the agent decides when to call Mnemo tools behind the scenes. After running `memory_setup`, the agent already knows when and how to use them.
|
|
64
|
+
|
|
65
|
+
### First-time setup
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
You: Help me set up Mnemo for memory management
|
|
69
|
+
Agent: I'll initialize Mnemo for you.
|
|
70
|
+
→ [calls memory_setup tool]
|
|
71
|
+
Mnemo has been initialized. I've added memory management
|
|
72
|
+
instructions to your AGENTS.md file.
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Saving memories (automatic)
|
|
76
|
+
|
|
77
|
+
The agent saves memories on its own when it recognizes important information:
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
You: Let's use 4-space indentation and single quotes for this project.
|
|
81
|
+
Agent: Got it. I'll follow that style.
|
|
82
|
+
→ [calls memory_save: "Project code style: 4-space indentation,
|
|
83
|
+
single quotes", tags: ["preference", "code-style"]]
|
|
84
|
+
I've saved this as a memory so I'll remember it next time.
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Recalling context (automatic)
|
|
88
|
+
|
|
89
|
+
When you start a new conversation, the agent searches for relevant memories:
|
|
90
|
+
|
|
91
|
+
```
|
|
92
|
+
You: Let's continue working on the auth module.
|
|
93
|
+
Agent: → [calls memory_search: "auth module"]
|
|
94
|
+
Based on my memories, last time we decided to use JWT with
|
|
95
|
+
refresh tokens and store them in httpOnly cookies. Let me
|
|
96
|
+
pick up from there.
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Searching memories (on request)
|
|
100
|
+
|
|
101
|
+
```
|
|
102
|
+
You: Do you remember what database we chose?
|
|
103
|
+
Agent: → [calls memory_search: "database choice"]
|
|
104
|
+
Yes — we decided on PostgreSQL with Prisma ORM, mainly for
|
|
105
|
+
its type safety and migration tooling.
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Compressing memories
|
|
109
|
+
|
|
110
|
+
When memories accumulate, you can ask the agent to clean up:
|
|
111
|
+
|
|
112
|
+
```
|
|
113
|
+
You: We have a lot of memories now. Can you clean them up?
|
|
114
|
+
Agent: → [calls memory_compress]
|
|
115
|
+
I found 23 memories. Let me distill them into fewer notes...
|
|
116
|
+
→ [calls memory_compress_apply: saves 8 distilled notes,
|
|
117
|
+
deletes 23 originals]
|
|
118
|
+
Done. Compressed 23 memories into 8 concise notes.
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Tools
|
|
122
|
+
|
|
123
|
+
Mnemo provides 6 MCP tools:
|
|
124
|
+
|
|
125
|
+
| Tool | Description |
|
|
126
|
+
| ----------------------- | --------------------------------------------------------------------------- |
|
|
127
|
+
| `memory_setup` | Initialize Mnemo — inject usage instructions into agent config |
|
|
128
|
+
| `memory_save` | Save a memory note with optional tags and source |
|
|
129
|
+
| `memory_search` | Semantic search across memories (supports `source_filter` and `tag_filter`) |
|
|
130
|
+
| `memory_compress` | List all notes for review/distillation |
|
|
131
|
+
| `memory_compress_apply` | Atomically save distilled notes and delete originals |
|
|
132
|
+
| `memory_delete` | Delete notes by ID |
|
|
133
|
+
|
|
134
|
+
## How It Works
|
|
135
|
+
|
|
136
|
+
### Storage
|
|
137
|
+
|
|
138
|
+
Memory notes are stored as Markdown files with YAML frontmatter:
|
|
139
|
+
|
|
140
|
+
```
|
|
141
|
+
~/Library/Application Support/mnemo/ # macOS
|
|
142
|
+
~/.local/share/mnemo/ # Linux
|
|
143
|
+
%APPDATA%/mnemo/ # Windows
|
|
144
|
+
├── notes/ # Markdown files
|
|
145
|
+
│ ├── 20260305-172200-a3f1.md
|
|
146
|
+
│ └── 20260305-183015-b7c2.md
|
|
147
|
+
└── index/ # Vector index (vectra)
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Override the data directory with `MNEMO_DATA_DIR` environment variable.
|
|
151
|
+
|
|
152
|
+
### Semantic Search
|
|
153
|
+
|
|
154
|
+
Mnemo uses [all-MiniLM-L6-v2](https://huggingface.co/Xenova/all-MiniLM-L6-v2) (33MB, 384 dimensions) for local embeddings via `@huggingface/transformers`. The model is preloaded at server startup so it's ready before the first search.
|
|
155
|
+
|
|
156
|
+
### Memory Lifecycle
|
|
157
|
+
|
|
158
|
+
1. **Save** — Agent saves key info during conversations (decisions, preferences, architecture choices, or when context is running low)
|
|
159
|
+
2. **Search** — Agent retrieves relevant context at the start of new conversations or when needed
|
|
160
|
+
3. **Compress** — When notes accumulate, the agent distills them into fewer, concise notes via `memory_compress` → review → `memory_compress_apply`
|
|
161
|
+
|
|
162
|
+
## Development
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
git clone git@github.com:See-Cat/mnemo.git
|
|
166
|
+
cd mnemo
|
|
167
|
+
npm install
|
|
168
|
+
npm run build
|
|
169
|
+
npm test
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Scripts
|
|
173
|
+
|
|
174
|
+
| Command | Description |
|
|
175
|
+
| ---------------------- | ---------------------------- |
|
|
176
|
+
| `npm run build` | Compile TypeScript |
|
|
177
|
+
| `npm run dev` | Watch mode compilation |
|
|
178
|
+
| `npm test` | Run tests (Vitest) |
|
|
179
|
+
| `npm run test:watch` | Watch mode tests |
|
|
180
|
+
| `npm run prettier:fix` | Format all files |
|
|
181
|
+
| `npm run release` | Interactive release workflow |
|
|
182
|
+
|
|
183
|
+
### Release
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
npm run release
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
Interactive script that walks through: git check → branch check → version selection → format → test → build → publish → push.
|
|
190
|
+
|
|
191
|
+
## License
|
|
192
|
+
|
|
193
|
+
MIT
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Supported agent tool types
|
|
3
|
+
*/
|
|
4
|
+
export declare const AGENT_TYPES: readonly ["opencode", "claude-code", "openclaw", "codex"];
|
|
5
|
+
export type AgentType = (typeof AGENT_TYPES)[number];
|
|
6
|
+
/**
|
|
7
|
+
* Get platform-appropriate data directory for Mnemo.
|
|
8
|
+
*
|
|
9
|
+
* Follows platform conventions:
|
|
10
|
+
* - macOS: ~/Library/Application Support/mnemo
|
|
11
|
+
* - Linux: $XDG_DATA_HOME/mnemo (defaults to ~/.local/share/mnemo)
|
|
12
|
+
* - Windows: %APPDATA%/mnemo
|
|
13
|
+
*
|
|
14
|
+
* Can be overridden via MNEMO_DATA_DIR env var.
|
|
15
|
+
*/
|
|
16
|
+
export declare function getDataDir(): string;
|
|
17
|
+
/**
|
|
18
|
+
* Get the notes directory
|
|
19
|
+
*/
|
|
20
|
+
export declare function getNotesDir(): string;
|
|
21
|
+
/**
|
|
22
|
+
* Get the vector index directory
|
|
23
|
+
*/
|
|
24
|
+
export declare function getIndexDir(): string;
|
|
25
|
+
/**
|
|
26
|
+
* Ensure a directory exists
|
|
27
|
+
*/
|
|
28
|
+
export declare function ensureDir(dir: string): Promise<void>;
|
|
29
|
+
/**
|
|
30
|
+
* Memory note metadata
|
|
31
|
+
*/
|
|
32
|
+
export interface NoteMeta {
|
|
33
|
+
id: string;
|
|
34
|
+
created: string;
|
|
35
|
+
updated: string;
|
|
36
|
+
tags: string[];
|
|
37
|
+
source: string;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Memory note (metadata + content)
|
|
41
|
+
*/
|
|
42
|
+
export interface Note {
|
|
43
|
+
meta: NoteMeta;
|
|
44
|
+
content: string;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Compress trigger thresholds
|
|
48
|
+
*/
|
|
49
|
+
export declare const COMPRESS_THRESHOLDS: {
|
|
50
|
+
/** Max number of notes before auto-compress suggestion */
|
|
51
|
+
readonly maxNotes: 50;
|
|
52
|
+
/** Max total content size (chars) before auto-compress suggestion */
|
|
53
|
+
readonly maxTotalSize: 100000;
|
|
54
|
+
};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import fs from 'node:fs/promises';
|
|
4
|
+
/**
|
|
5
|
+
* Supported agent tool types
|
|
6
|
+
*/
|
|
7
|
+
export const AGENT_TYPES = ['opencode', 'claude-code', 'openclaw', 'codex'];
|
|
8
|
+
/**
|
|
9
|
+
* Get platform-appropriate data directory for Mnemo.
|
|
10
|
+
*
|
|
11
|
+
* Follows platform conventions:
|
|
12
|
+
* - macOS: ~/Library/Application Support/mnemo
|
|
13
|
+
* - Linux: $XDG_DATA_HOME/mnemo (defaults to ~/.local/share/mnemo)
|
|
14
|
+
* - Windows: %APPDATA%/mnemo
|
|
15
|
+
*
|
|
16
|
+
* Can be overridden via MNEMO_DATA_DIR env var.
|
|
17
|
+
*/
|
|
18
|
+
export function getDataDir() {
|
|
19
|
+
const envDir = process.env.MNEMO_DATA_DIR;
|
|
20
|
+
if (envDir)
|
|
21
|
+
return envDir;
|
|
22
|
+
const platform = process.platform;
|
|
23
|
+
const home = os.homedir();
|
|
24
|
+
switch (platform) {
|
|
25
|
+
case 'darwin':
|
|
26
|
+
return path.join(home, 'Library', 'Application Support', 'mnemo');
|
|
27
|
+
case 'win32':
|
|
28
|
+
return path.join(process.env.APPDATA || path.join(home, 'AppData', 'Roaming'), 'mnemo');
|
|
29
|
+
default:
|
|
30
|
+
// Linux and others: follow XDG Base Directory spec
|
|
31
|
+
return path.join(process.env.XDG_DATA_HOME || path.join(home, '.local', 'share'), 'mnemo');
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Get the notes directory
|
|
36
|
+
*/
|
|
37
|
+
export function getNotesDir() {
|
|
38
|
+
return path.join(getDataDir(), 'notes');
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Get the vector index directory
|
|
42
|
+
*/
|
|
43
|
+
export function getIndexDir() {
|
|
44
|
+
return path.join(getDataDir(), 'index');
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Ensure a directory exists
|
|
48
|
+
*/
|
|
49
|
+
export async function ensureDir(dir) {
|
|
50
|
+
await fs.mkdir(dir, { recursive: true });
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Compress trigger thresholds
|
|
54
|
+
*/
|
|
55
|
+
export const COMPRESS_THRESHOLDS = {
|
|
56
|
+
/** Max number of notes before auto-compress suggestion */
|
|
57
|
+
maxNotes: 50,
|
|
58
|
+
/** Max total content size (chars) before auto-compress suggestion */
|
|
59
|
+
maxTotalSize: 100_000,
|
|
60
|
+
};
|
|
61
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/core/config.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAElC;;GAEG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,UAAU,EAAE,aAAa,EAAE,UAAU,EAAE,OAAO,CAAU,CAAC;AAIrF;;;;;;;;;GASG;AACH,MAAM,UAAU,UAAU;IACtB,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;IAC1C,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC;IAE1B,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;IAClC,MAAM,IAAI,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC;IAE1B,QAAQ,QAAQ,EAAE,CAAC;QACf,KAAK,QAAQ;YACT,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,qBAAqB,EAAE,OAAO,CAAC,CAAC;QACtE,KAAK,OAAO;YACR,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,SAAS,CAAC,EAAE,OAAO,CAAC,CAAC;QAC5F;YACI,mDAAmD;YACnD,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,CAAC,EAAE,OAAO,CAAC,CAAC;IACnG,CAAC;AACL,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,WAAW;IACvB,OAAO,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,EAAE,OAAO,CAAC,CAAC;AAC5C,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,WAAW;IACvB,OAAO,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,EAAE,OAAO,CAAC,CAAC;AAC5C,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,GAAW;IACvC,MAAM,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;AAC7C,CAAC;AAqBD;;GAEG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG;IAC/B,0DAA0D;IAC1D,QAAQ,EAAE,EAAE;IACZ,qEAAqE;IACrE,YAAY,EAAE,OAAO;CACf,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { type Note } from './config.js';
|
|
2
|
+
/**
|
|
3
|
+
* Pre-warm the embedding model in the background.
|
|
4
|
+
* Call this at server startup so the model is ready before the first tool call.
|
|
5
|
+
*/
|
|
6
|
+
export declare function preloadEmbedding(): void;
|
|
7
|
+
/**
|
|
8
|
+
* Check if the embedding model is ready
|
|
9
|
+
*/
|
|
10
|
+
export declare function isEmbeddingReady(): boolean;
|
|
11
|
+
/**
|
|
12
|
+
* Generate embedding vector for text
|
|
13
|
+
*/
|
|
14
|
+
export declare function embed(text: string): Promise<number[]>;
|
|
15
|
+
/**
|
|
16
|
+
* Index a note (generate embedding and store in vector index)
|
|
17
|
+
*/
|
|
18
|
+
export declare function indexNote(note: Note): Promise<void>;
|
|
19
|
+
/**
|
|
20
|
+
* Remove a note from the vector index
|
|
21
|
+
*/
|
|
22
|
+
export declare function removeFromIndex(noteId: string): Promise<void>;
|
|
23
|
+
/**
|
|
24
|
+
* Remove multiple notes from the vector index
|
|
25
|
+
*/
|
|
26
|
+
export declare function removeMultipleFromIndex(noteIds: string[]): Promise<void>;
|
|
27
|
+
/**
|
|
28
|
+
* Search for similar notes by query text
|
|
29
|
+
*/
|
|
30
|
+
export declare function searchNotes(query: string, topK?: number, sourceFilter?: string): Promise<Array<{
|
|
31
|
+
id: string;
|
|
32
|
+
score: number;
|
|
33
|
+
text: string;
|
|
34
|
+
tags: string;
|
|
35
|
+
source: string;
|
|
36
|
+
created: string;
|
|
37
|
+
}>>;
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { LocalIndex } from 'vectra';
|
|
2
|
+
import { getIndexDir, ensureDir } from './config.js';
|
|
3
|
+
let embedder = null;
|
|
4
|
+
let embedderLoading = null;
|
|
5
|
+
let indexInstance = null;
|
|
6
|
+
/**
|
|
7
|
+
* Load the embedding model.
|
|
8
|
+
* Uses all-MiniLM-L6-v2 (384 dims, ~33MB, good quality/speed tradeoff).
|
|
9
|
+
* Deduplicates concurrent calls so the model is only loaded once.
|
|
10
|
+
*/
|
|
11
|
+
async function getEmbedder() {
|
|
12
|
+
if (embedder)
|
|
13
|
+
return embedder;
|
|
14
|
+
if (!embedderLoading) {
|
|
15
|
+
embedderLoading = (async () => {
|
|
16
|
+
console.error('Mnemo: loading embedding model...');
|
|
17
|
+
const { pipeline } = await import('@huggingface/transformers');
|
|
18
|
+
embedder = await pipeline('feature-extraction', 'Xenova/all-MiniLM-L6-v2');
|
|
19
|
+
console.error('Mnemo: embedding model loaded');
|
|
20
|
+
return embedder;
|
|
21
|
+
})();
|
|
22
|
+
}
|
|
23
|
+
return embedderLoading;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Pre-warm the embedding model in the background.
|
|
27
|
+
* Call this at server startup so the model is ready before the first tool call.
|
|
28
|
+
*/
|
|
29
|
+
export function preloadEmbedding() {
|
|
30
|
+
getEmbedder().catch((err) => {
|
|
31
|
+
console.error('Mnemo: failed to preload embedding model:', err);
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Check if the embedding model is ready
|
|
36
|
+
*/
|
|
37
|
+
export function isEmbeddingReady() {
|
|
38
|
+
return embedder !== null;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Generate embedding vector for text
|
|
42
|
+
*/
|
|
43
|
+
export async function embed(text) {
|
|
44
|
+
const model = await getEmbedder();
|
|
45
|
+
const output = await model(text, { pooling: 'mean', normalize: true });
|
|
46
|
+
return Array.from(output.data);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Get or create the vector index
|
|
50
|
+
*/
|
|
51
|
+
async function getIndex() {
|
|
52
|
+
if (!indexInstance) {
|
|
53
|
+
const indexDir = getIndexDir();
|
|
54
|
+
await ensureDir(indexDir);
|
|
55
|
+
indexInstance = new LocalIndex(indexDir);
|
|
56
|
+
if (!(await indexInstance.isIndexCreated())) {
|
|
57
|
+
await indexInstance.createIndex({
|
|
58
|
+
version: 1,
|
|
59
|
+
metadata_config: { indexed: ['source'] },
|
|
60
|
+
});
|
|
61
|
+
console.error('Mnemo: vector index created');
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return indexInstance;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Index a note (generate embedding and store in vector index)
|
|
68
|
+
*/
|
|
69
|
+
export async function indexNote(note) {
|
|
70
|
+
const index = await getIndex();
|
|
71
|
+
const vector = await embed(note.content);
|
|
72
|
+
await index.insertItem({
|
|
73
|
+
id: note.meta.id,
|
|
74
|
+
vector,
|
|
75
|
+
metadata: {
|
|
76
|
+
id: note.meta.id,
|
|
77
|
+
text: note.content.slice(0, 500), // store truncated text for quick access
|
|
78
|
+
tags: note.meta.tags.join(','),
|
|
79
|
+
source: note.meta.source,
|
|
80
|
+
created: note.meta.created,
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Remove a note from the vector index
|
|
86
|
+
*/
|
|
87
|
+
export async function removeFromIndex(noteId) {
|
|
88
|
+
const index = await getIndex();
|
|
89
|
+
const item = await index.getItem(noteId);
|
|
90
|
+
if (item) {
|
|
91
|
+
await index.deleteItem(noteId);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Remove multiple notes from the vector index
|
|
96
|
+
*/
|
|
97
|
+
export async function removeMultipleFromIndex(noteIds) {
|
|
98
|
+
for (const id of noteIds) {
|
|
99
|
+
await removeFromIndex(id);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Search for similar notes by query text
|
|
104
|
+
*/
|
|
105
|
+
export async function searchNotes(query, topK = 5, sourceFilter) {
|
|
106
|
+
const index = await getIndex();
|
|
107
|
+
const vector = await embed(query);
|
|
108
|
+
const filter = sourceFilter ? { source: { $eq: sourceFilter } } : undefined;
|
|
109
|
+
const results = await index.queryItems(vector, '', topK, filter);
|
|
110
|
+
return results.map((r) => ({
|
|
111
|
+
id: r.item.metadata.id,
|
|
112
|
+
score: r.score,
|
|
113
|
+
text: r.item.metadata.text,
|
|
114
|
+
tags: r.item.metadata.tags,
|
|
115
|
+
source: r.item.metadata.source,
|
|
116
|
+
created: r.item.metadata.created,
|
|
117
|
+
}));
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=embedding.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"embedding.js","sourceRoot":"","sources":["../../src/core/embedding.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type Note } from './config.js';
|
|
2
|
+
/**
|
|
3
|
+
* Parse a note markdown file into Note object
|
|
4
|
+
*/
|
|
5
|
+
export declare function parseNote(raw: string): Note | null;
|
|
6
|
+
/**
|
|
7
|
+
* Serialize a Note to markdown string
|
|
8
|
+
*/
|
|
9
|
+
export declare function serializeNote(note: Note): string;
|
|
10
|
+
/**
|
|
11
|
+
* Save a new note to disk and return it
|
|
12
|
+
*/
|
|
13
|
+
export declare function saveNote(content: string, tags?: string[], source?: string): Promise<Note>;
|
|
14
|
+
/**
|
|
15
|
+
* Read a single note by ID
|
|
16
|
+
*/
|
|
17
|
+
export declare function readNote(id: string): Promise<Note | null>;
|
|
18
|
+
/**
|
|
19
|
+
* Read all notes from disk
|
|
20
|
+
*/
|
|
21
|
+
export declare function readAllNotes(): Promise<Note[]>;
|
|
22
|
+
/**
|
|
23
|
+
* Delete a note by ID
|
|
24
|
+
*/
|
|
25
|
+
export declare function deleteNote(id: string): Promise<boolean>;
|
|
26
|
+
/**
|
|
27
|
+
* Delete multiple notes by ID
|
|
28
|
+
*/
|
|
29
|
+
export declare function deleteNotes(ids: string[]): Promise<number>;
|
|
30
|
+
/**
|
|
31
|
+
* Get note stats (count and total size)
|
|
32
|
+
*/
|
|
33
|
+
export declare function getNoteStats(): Promise<{
|
|
34
|
+
count: number;
|
|
35
|
+
totalSize: number;
|
|
36
|
+
}>;
|