@jcyamacho/agent-memory 0.0.15 → 0.0.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 +37 -109
- package/dist/index.js +128 -42
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -3,16 +3,16 @@
|
|
|
3
3
|
Persistent memory for MCP-powered coding agents.
|
|
4
4
|
|
|
5
5
|
`agent-memory` is a stdio MCP server that gives your LLM durable memory backed
|
|
6
|
-
by SQLite. It
|
|
6
|
+
by SQLite. It helps your agent remember preferences, project context, and prior
|
|
7
|
+
decisions across sessions.
|
|
8
|
+
|
|
9
|
+
It exposes four tools:
|
|
7
10
|
|
|
8
11
|
- `remember` -> save facts, decisions, preferences, and project context
|
|
9
12
|
- `recall` -> retrieve the most relevant memories later
|
|
10
13
|
- `revise` -> update an existing memory when it becomes outdated
|
|
11
14
|
- `forget` -> delete a memory that is no longer relevant
|
|
12
15
|
|
|
13
|
-
Use it when your agent should remember preferences, project facts, and prior
|
|
14
|
-
decisions across sessions.
|
|
15
|
-
|
|
16
16
|
## Quick Start
|
|
17
17
|
|
|
18
18
|
Claude CLI:
|
|
@@ -27,104 +27,56 @@ Codex CLI:
|
|
|
27
27
|
codex mcp add memory -- npx -y @jcyamacho/agent-memory
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
```json
|
|
33
|
-
{
|
|
34
|
-
"mcpServers": {
|
|
35
|
-
"memory": {
|
|
36
|
-
"command": "npx",
|
|
37
|
-
"args": [
|
|
38
|
-
"-y",
|
|
39
|
-
"@jcyamacho/agent-memory"
|
|
40
|
-
]
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
With a custom database path:
|
|
30
|
+
OpenCode:
|
|
47
31
|
|
|
48
|
-
```
|
|
32
|
+
```jsonc
|
|
49
33
|
{
|
|
50
|
-
"
|
|
34
|
+
"$schema": "https://opencode.ai/config.json",
|
|
35
|
+
"mcp": {
|
|
51
36
|
"memory": {
|
|
52
|
-
"
|
|
53
|
-
"
|
|
54
|
-
"-y",
|
|
55
|
-
"@jcyamacho/agent-memory"
|
|
56
|
-
],
|
|
57
|
-
"env": {
|
|
58
|
-
"AGENT_MEMORY_DB_PATH": "/absolute/path/to/memory.db"
|
|
59
|
-
}
|
|
37
|
+
"type": "local",
|
|
38
|
+
"command": ["npx", "-y", "@jcyamacho/agent-memory"]
|
|
60
39
|
}
|
|
61
40
|
}
|
|
62
41
|
}
|
|
63
42
|
```
|
|
64
43
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
```json
|
|
68
|
-
{
|
|
69
|
-
"mcpServers": {
|
|
70
|
-
"memory": {
|
|
71
|
-
"command": "npx",
|
|
72
|
-
"args": [
|
|
73
|
-
"-y",
|
|
74
|
-
"@jcyamacho/agent-memory"
|
|
75
|
-
],
|
|
76
|
-
"env": {
|
|
77
|
-
"AGENT_MEMORY_MODELS_CACHE_PATH": "/absolute/path/to/models"
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
```
|
|
44
|
+
## Optional LLM Instructions
|
|
83
45
|
|
|
84
46
|
Optional LLM instructions to reinforce the MCP's built-in guidance:
|
|
85
47
|
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
48
|
+
```md
|
|
49
|
+
## Agent Memory
|
|
50
|
+
|
|
51
|
+
- Use `memory_recall` at conversation start and before design choices,
|
|
52
|
+
conventions, or edge cases.
|
|
53
|
+
- Query `memory_recall` with 2-5 short anchor-heavy terms or exact phrases,
|
|
54
|
+
not full questions or sentences.
|
|
55
|
+
- Pass `workspace` for project-scoped memory. Omit it only for truly global memories.
|
|
56
|
+
- Use `memory_remember` to save one durable fact when the user states a stable
|
|
57
|
+
preference, correction, or reusable project decision.
|
|
58
|
+
- If the fact already exists, use `memory_revise` instead of creating a duplicate.
|
|
59
|
+
- Use `memory_forget` to remove a wrong or obsolete memory.
|
|
60
|
+
- Do not store secrets or temporary task state in memory.
|
|
93
61
|
```
|
|
94
62
|
|
|
95
|
-
## What It Stores
|
|
96
|
-
|
|
97
|
-
This MCP is useful for context that should survive across turns and sessions:
|
|
98
|
-
|
|
99
|
-
- User preferences like response style, formatting, and workflow habits
|
|
100
|
-
- Project facts like paths, architecture choices, and conventions
|
|
101
|
-
- Important decisions and constraints that should not be rediscovered
|
|
102
|
-
- Project-scoped notes that still matter later
|
|
103
|
-
|
|
104
63
|
## Web UI
|
|
105
64
|
|
|
106
65
|
Browse, edit, and delete memories in a local web interface:
|
|
107
66
|
|
|
108
67
|
```bash
|
|
109
|
-
npx -y @jcyamacho/agent-memory --ui
|
|
68
|
+
npx -y @jcyamacho/agent-memory@latest --ui
|
|
110
69
|
```
|
|
111
70
|
|
|
112
71
|
Opens at `http://localhost:6580`. Use `--port` to change:
|
|
113
72
|
|
|
114
73
|
```bash
|
|
115
|
-
npx -y @jcyamacho/agent-memory --ui --port 9090
|
|
74
|
+
npx -y @jcyamacho/agent-memory@latest --ui --port 9090
|
|
116
75
|
```
|
|
117
76
|
|
|
118
77
|
The web UI uses the same database as the MCP server.
|
|
119
78
|
|
|
120
|
-
##
|
|
121
|
-
|
|
122
|
-
- `remember` saves durable facts, preferences, decisions, and project context.
|
|
123
|
-
- `recall` retrieves the most relevant saved memories.
|
|
124
|
-
- `revise` updates an existing memory when it becomes outdated.
|
|
125
|
-
- `forget` deletes a memory that is no longer relevant.
|
|
126
|
-
|
|
127
|
-
## How Ranking Works
|
|
79
|
+
## How Recall Finds Memories
|
|
128
80
|
|
|
129
81
|
`recall` uses a multi-signal ranking system to surface the most relevant
|
|
130
82
|
memories:
|
|
@@ -147,7 +99,13 @@ If you omit `workspace`, recall still uses text relevance, embedding similarity,
|
|
|
147
99
|
and recency. For best results, pass `workspace` whenever you have one. Save
|
|
148
100
|
memories without a workspace only when they apply across all projects.
|
|
149
101
|
|
|
150
|
-
|
|
102
|
+
When you save a memory from a git worktree, `agent-memory` stores the main repo
|
|
103
|
+
root as the workspace. `recall` applies the same normalization to incoming
|
|
104
|
+
workspace queries so linked worktrees still match repo-scoped memories exactly.
|
|
105
|
+
|
|
106
|
+
## Configuration
|
|
107
|
+
|
|
108
|
+
### Database Location
|
|
151
109
|
|
|
152
110
|
By default, the SQLite database is created at:
|
|
153
111
|
|
|
@@ -167,7 +125,7 @@ Set `AGENT_MEMORY_DB_PATH` when you want to:
|
|
|
167
125
|
- share a memory DB across multiple clients
|
|
168
126
|
- store the DB somewhere easier to back up or inspect
|
|
169
127
|
|
|
170
|
-
|
|
128
|
+
### Model Cache Location
|
|
171
129
|
|
|
172
130
|
By default, downloaded embedding model files are cached at:
|
|
173
131
|
|
|
@@ -187,38 +145,8 @@ Set `AGENT_MEMORY_MODELS_CACHE_PATH` when you want to:
|
|
|
187
145
|
- share the model cache across reinstalls or multiple clients
|
|
188
146
|
- store model downloads somewhere easier to inspect or manage
|
|
189
147
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
## Development
|
|
194
|
-
|
|
195
|
-
For working on the project itself or running from source. Requires Bun and
|
|
196
|
-
Node.js.
|
|
197
|
-
|
|
198
|
-
```bash
|
|
199
|
-
bun install
|
|
200
|
-
bun run build
|
|
201
|
-
```
|
|
202
|
-
|
|
203
|
-
To use a local build as your MCP server:
|
|
204
|
-
|
|
205
|
-
```json
|
|
206
|
-
{
|
|
207
|
-
"mcpServers": {
|
|
208
|
-
"memory": {
|
|
209
|
-
"command": "node",
|
|
210
|
-
"args": [
|
|
211
|
-
"/absolute/path/to/agent-memory/dist/index.js"
|
|
212
|
-
]
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
```
|
|
217
|
-
|
|
218
|
-
```bash
|
|
219
|
-
bun lint
|
|
220
|
-
bun test
|
|
221
|
-
```
|
|
148
|
+
Schema changes are migrated automatically, including workspace normalization for
|
|
149
|
+
existing git worktree memories when the original path can still be resolved.
|
|
222
150
|
|
|
223
151
|
## License
|
|
224
152
|
|
package/dist/index.js
CHANGED
|
@@ -12464,7 +12464,7 @@ class StdioServerTransport {
|
|
|
12464
12464
|
}
|
|
12465
12465
|
}
|
|
12466
12466
|
// package.json
|
|
12467
|
-
var version2 = "0.0.
|
|
12467
|
+
var version2 = "0.0.18";
|
|
12468
12468
|
|
|
12469
12469
|
// src/config.ts
|
|
12470
12470
|
import { homedir } from "node:os";
|
|
@@ -20058,11 +20058,17 @@ function parseOptionalDate(value, fieldName) {
|
|
|
20058
20058
|
|
|
20059
20059
|
// src/mcp/tools/forget.ts
|
|
20060
20060
|
var forgetInputSchema = {
|
|
20061
|
-
id: string2().describe("
|
|
20061
|
+
id: string2().describe("Memory id to delete. Use an id returned by `recall`.")
|
|
20062
20062
|
};
|
|
20063
20063
|
function registerForgetTool(server, memory) {
|
|
20064
20064
|
server.registerTool("forget", {
|
|
20065
|
-
|
|
20065
|
+
annotations: {
|
|
20066
|
+
title: "Forget",
|
|
20067
|
+
destructiveHint: true,
|
|
20068
|
+
idempotentHint: true,
|
|
20069
|
+
openWorldHint: false
|
|
20070
|
+
},
|
|
20071
|
+
description: 'Delete a memory that is wrong or obsolete. Use after `recall` when you have the memory id. Use `revise` instead if the fact should remain with corrected wording. Returns `<memory id="..." deleted="true" />`.',
|
|
20066
20072
|
inputSchema: forgetInputSchema
|
|
20067
20073
|
}, async ({ id }) => {
|
|
20068
20074
|
try {
|
|
@@ -20146,19 +20152,22 @@ var MAX_LIST_LIMIT = 100;
|
|
|
20146
20152
|
class MemoryService {
|
|
20147
20153
|
repository;
|
|
20148
20154
|
embeddingService;
|
|
20149
|
-
|
|
20155
|
+
workspaceResolver;
|
|
20156
|
+
constructor(repository, embeddingService, workspaceResolver) {
|
|
20150
20157
|
this.repository = repository;
|
|
20151
20158
|
this.embeddingService = embeddingService;
|
|
20159
|
+
this.workspaceResolver = workspaceResolver;
|
|
20152
20160
|
}
|
|
20153
20161
|
async create(input) {
|
|
20154
20162
|
const content = input.content.trim();
|
|
20155
20163
|
if (!content) {
|
|
20156
20164
|
throw new ValidationError("Memory content is required.");
|
|
20157
20165
|
}
|
|
20166
|
+
const workspace = await this.workspaceResolver.resolve(input.workspace);
|
|
20158
20167
|
const memory = await this.repository.create({
|
|
20159
20168
|
content,
|
|
20160
20169
|
embedding: await this.embeddingService.createVector(content),
|
|
20161
|
-
workspace
|
|
20170
|
+
workspace
|
|
20162
20171
|
});
|
|
20163
20172
|
return toPublicMemoryRecord(memory);
|
|
20164
20173
|
}
|
|
@@ -20184,7 +20193,7 @@ class MemoryService {
|
|
|
20184
20193
|
return memory ? toPublicMemoryRecord(memory) : undefined;
|
|
20185
20194
|
}
|
|
20186
20195
|
async list(input) {
|
|
20187
|
-
const workspace =
|
|
20196
|
+
const workspace = await this.workspaceResolver.resolve(input.workspace);
|
|
20188
20197
|
const page = await this.repository.list({
|
|
20189
20198
|
workspace,
|
|
20190
20199
|
workspaceIsNull: workspace ? false : Boolean(input.workspaceIsNull),
|
|
@@ -20202,7 +20211,7 @@ class MemoryService {
|
|
|
20202
20211
|
throw new ValidationError("At least one search term is required.");
|
|
20203
20212
|
}
|
|
20204
20213
|
const requestedLimit = normalizeLimit(input.limit);
|
|
20205
|
-
const workspace =
|
|
20214
|
+
const workspace = await this.workspaceResolver.resolve(input.workspace);
|
|
20206
20215
|
const normalizedQuery = {
|
|
20207
20216
|
terms,
|
|
20208
20217
|
limit: requestedLimit * RECALL_CANDIDATE_LIMIT_MULTIPLIER,
|
|
@@ -20259,10 +20268,6 @@ function normalizeListLimit(value) {
|
|
|
20259
20268
|
}
|
|
20260
20269
|
return Math.min(Math.max(value, 1), MAX_LIST_LIMIT);
|
|
20261
20270
|
}
|
|
20262
|
-
function normalizeOptionalString(value) {
|
|
20263
|
-
const trimmed = value?.trim();
|
|
20264
|
-
return trimmed ? trimmed : undefined;
|
|
20265
|
-
}
|
|
20266
20271
|
function normalizeTerms(terms) {
|
|
20267
20272
|
const normalizedTerms = terms.map((term) => term.trim()).filter(Boolean);
|
|
20268
20273
|
return [...new Set(normalizedTerms)];
|
|
@@ -20270,11 +20275,11 @@ function normalizeTerms(terms) {
|
|
|
20270
20275
|
|
|
20271
20276
|
// src/mcp/tools/recall.ts
|
|
20272
20277
|
var recallInputSchema = {
|
|
20273
|
-
terms: array(string2()).min(1).describe("
|
|
20278
|
+
terms: array(string2()).min(1).describe("Pass 2-5 short anchor-heavy terms or exact phrases as separate entries. Prefer identifiers, commands, file paths, package names, and conventions likely to appear verbatim. Avoid vague words, full questions, and repeating the workspace name."),
|
|
20274
20279
|
limit: number2().int().min(1).max(MAX_RECALL_LIMIT).optional().describe("Maximum matches to return. Keep this small when you only need the strongest hits."),
|
|
20275
|
-
workspace: string2().optional().describe("Pass the current working directory
|
|
20276
|
-
updated_after: string2().optional().describe("Only return memories updated
|
|
20277
|
-
updated_before: string2().optional().describe("Only return memories updated
|
|
20280
|
+
workspace: string2().optional().describe("Pass the current working directory to prefer memories from the active project. Git worktree paths are normalized to the main repo root for matching."),
|
|
20281
|
+
updated_after: string2().optional().describe("Only return memories updated on or after this ISO 8601 timestamp."),
|
|
20282
|
+
updated_before: string2().optional().describe("Only return memories updated on or before this ISO 8601 timestamp.")
|
|
20278
20283
|
};
|
|
20279
20284
|
function toMemoryXml(r) {
|
|
20280
20285
|
const workspace = r.workspace ? ` workspace="${escapeXml(r.workspace)}"` : "";
|
|
@@ -20286,7 +20291,12 @@ ${content}
|
|
|
20286
20291
|
}
|
|
20287
20292
|
function registerRecallTool(server, memory) {
|
|
20288
20293
|
server.registerTool("recall", {
|
|
20289
|
-
|
|
20294
|
+
annotations: {
|
|
20295
|
+
title: "Recall",
|
|
20296
|
+
readOnlyHint: true,
|
|
20297
|
+
openWorldHint: false
|
|
20298
|
+
},
|
|
20299
|
+
description: "Find memories relevant to the current task or check whether a fact already exists before saving. Use at conversation start and before design choices. Pass short anchor-heavy `terms` and `workspace` when available. Returns `<memories>...</memories>` or a no-match hint.",
|
|
20290
20300
|
inputSchema: recallInputSchema
|
|
20291
20301
|
}, async ({ terms, limit, workspace, updated_after, updated_before }) => {
|
|
20292
20302
|
try {
|
|
@@ -20297,7 +20307,7 @@ function registerRecallTool(server, memory) {
|
|
|
20297
20307
|
updatedAfter: parseOptionalDate(updated_after, "updated_after"),
|
|
20298
20308
|
updatedBefore: parseOptionalDate(updated_before, "updated_before")
|
|
20299
20309
|
});
|
|
20300
|
-
const text = results.length === 0 ? "No matching memories found. Retry once with 1-3 alternate
|
|
20310
|
+
const text = results.length === 0 ? "No matching memories found. Retry once with 1-3 overlapping alternate terms or an exact identifier, command, file path, or phrase likely to appear in the memory." : `<memories>
|
|
20301
20311
|
${results.map(toMemoryXml).join(`
|
|
20302
20312
|
`)}
|
|
20303
20313
|
</memories>`;
|
|
@@ -20312,12 +20322,18 @@ ${results.map(toMemoryXml).join(`
|
|
|
20312
20322
|
|
|
20313
20323
|
// src/mcp/tools/remember.ts
|
|
20314
20324
|
var rememberInputSchema = {
|
|
20315
|
-
content: string2().describe("One durable fact to save. Use a
|
|
20316
|
-
workspace: string2().optional().describe("Pass the current working directory for project-
|
|
20325
|
+
content: string2().describe("One new durable fact to save. Use a self-contained sentence or short note."),
|
|
20326
|
+
workspace: string2().optional().describe("Pass the current working directory for project-scoped memory. Git worktree paths are saved as the main repo root. Omit for truly global memory.")
|
|
20317
20327
|
};
|
|
20318
20328
|
function registerRememberTool(server, memory) {
|
|
20319
20329
|
server.registerTool("remember", {
|
|
20320
|
-
|
|
20330
|
+
annotations: {
|
|
20331
|
+
title: "Remember",
|
|
20332
|
+
destructiveHint: false,
|
|
20333
|
+
idempotentHint: false,
|
|
20334
|
+
openWorldHint: false
|
|
20335
|
+
},
|
|
20336
|
+
description: 'Save one new durable fact for later recall. Use for stable preferences, corrections, reusable decisions, and project context not obvious from code or git history. Save exactly one fact. If the memory already exists, use `revise` instead. Do not store secrets, temporary task state, or facts obvious from code or git history. Returns `<memory id="..." />`.',
|
|
20321
20337
|
inputSchema: rememberInputSchema
|
|
20322
20338
|
}, async ({ content, workspace }) => {
|
|
20323
20339
|
try {
|
|
@@ -20336,12 +20352,18 @@ function registerRememberTool(server, memory) {
|
|
|
20336
20352
|
|
|
20337
20353
|
// src/mcp/tools/revise.ts
|
|
20338
20354
|
var reviseInputSchema = {
|
|
20339
|
-
id: string2().describe("
|
|
20340
|
-
content: string2().describe("
|
|
20355
|
+
id: string2().describe("Memory id to update. Use an id returned by `recall`."),
|
|
20356
|
+
content: string2().describe("Corrected replacement text for that memory. Keep it to one durable fact.")
|
|
20341
20357
|
};
|
|
20342
20358
|
function registerReviseTool(server, memory) {
|
|
20343
20359
|
server.registerTool("revise", {
|
|
20344
|
-
|
|
20360
|
+
annotations: {
|
|
20361
|
+
title: "Revise",
|
|
20362
|
+
destructiveHint: false,
|
|
20363
|
+
idempotentHint: false,
|
|
20364
|
+
openWorldHint: false
|
|
20365
|
+
},
|
|
20366
|
+
description: 'Update one existing memory when the same fact still applies but its wording or details changed. Use after `recall` when you already have the memory id. Keep it to one fact and do not merge unrelated facts. Returns `<memory id="..." updated_at="..." />`.',
|
|
20345
20367
|
inputSchema: reviseInputSchema
|
|
20346
20368
|
}, async ({ id, content }) => {
|
|
20347
20369
|
try {
|
|
@@ -20362,15 +20384,12 @@ function registerReviseTool(server, memory) {
|
|
|
20362
20384
|
|
|
20363
20385
|
// src/mcp/server.ts
|
|
20364
20386
|
var SERVER_INSTRUCTIONS = [
|
|
20365
|
-
"Use this server for durable memory:
|
|
20366
|
-
"Use `recall` at conversation start
|
|
20367
|
-
"
|
|
20368
|
-
"`
|
|
20369
|
-
"
|
|
20370
|
-
"
|
|
20371
|
-
"Call `recall` before `remember`; if the fact already exists, use `revise` instead of creating a duplicate.",
|
|
20372
|
-
"Use `revise` to correct an existing memory and `forget` to remove a wrong or obsolete one.",
|
|
20373
|
-
"Pass workspace for project-scoped calls. Omit it only for truly global memories."
|
|
20387
|
+
"Use this server only for durable memory that should survive across turns: stable preferences, corrections, reusable decisions, and project context not obvious from code or git history.",
|
|
20388
|
+
"Use `recall` at conversation start, before design choices, and before saving memory.",
|
|
20389
|
+
"Use `remember` only for a new durable fact. Use `revise` when the fact already exists but needs correction.",
|
|
20390
|
+
"Use `forget` only when a memory is wrong or no longer relevant.",
|
|
20391
|
+
"Pass workspace for project-scoped memory. Omit it only for truly global memory.",
|
|
20392
|
+
"Do not store secrets or temporary task state in memory."
|
|
20374
20393
|
].join(" ");
|
|
20375
20394
|
function createMcpServer(memory, version3) {
|
|
20376
20395
|
const server = new McpServer({
|
|
@@ -20502,7 +20521,7 @@ function toUint8Array(value) {
|
|
|
20502
20521
|
}
|
|
20503
20522
|
|
|
20504
20523
|
// src/sqlite/migrations/002-add-memory-embedding.ts
|
|
20505
|
-
function createAddMemoryEmbeddingMigration(embeddingService
|
|
20524
|
+
function createAddMemoryEmbeddingMigration(embeddingService) {
|
|
20506
20525
|
return {
|
|
20507
20526
|
version: 2,
|
|
20508
20527
|
async up(database) {
|
|
@@ -20531,13 +20550,33 @@ function createAddMemoryEmbeddingMigration(embeddingService = new EmbeddingServi
|
|
|
20531
20550
|
}
|
|
20532
20551
|
};
|
|
20533
20552
|
}
|
|
20534
|
-
|
|
20553
|
+
|
|
20554
|
+
// src/sqlite/migrations/003-normalize-workspaces.ts
|
|
20555
|
+
function createNormalizeWorkspaceMigration(workspaceResolver) {
|
|
20556
|
+
return {
|
|
20557
|
+
version: 3,
|
|
20558
|
+
async up(database) {
|
|
20559
|
+
const rows = database.prepare("SELECT DISTINCT workspace FROM memories WHERE workspace IS NOT NULL ORDER BY workspace").all();
|
|
20560
|
+
const updateStatement = database.prepare("UPDATE memories SET workspace = ? WHERE workspace = ?");
|
|
20561
|
+
for (const row of rows) {
|
|
20562
|
+
const normalizedWorkspace = await workspaceResolver.resolve(row.workspace);
|
|
20563
|
+
if (!normalizedWorkspace || normalizedWorkspace === row.workspace) {
|
|
20564
|
+
continue;
|
|
20565
|
+
}
|
|
20566
|
+
updateStatement.run(normalizedWorkspace, row.workspace);
|
|
20567
|
+
}
|
|
20568
|
+
}
|
|
20569
|
+
};
|
|
20570
|
+
}
|
|
20535
20571
|
|
|
20536
20572
|
// src/sqlite/migrations/index.ts
|
|
20537
|
-
function createMemoryMigrations(
|
|
20538
|
-
return [
|
|
20573
|
+
function createMemoryMigrations(options) {
|
|
20574
|
+
return [
|
|
20575
|
+
createMemorySchemaMigration,
|
|
20576
|
+
createAddMemoryEmbeddingMigration(options.embeddingService),
|
|
20577
|
+
createNormalizeWorkspaceMigration(options.workspaceResolver)
|
|
20578
|
+
];
|
|
20539
20579
|
}
|
|
20540
|
-
var MEMORY_MIGRATIONS = createMemoryMigrations();
|
|
20541
20580
|
|
|
20542
20581
|
// src/sqlite/db.ts
|
|
20543
20582
|
var PRAGMA_STATEMENTS = [
|
|
@@ -20546,12 +20585,12 @@ var PRAGMA_STATEMENTS = [
|
|
|
20546
20585
|
"foreign_keys = ON",
|
|
20547
20586
|
"busy_timeout = 5000"
|
|
20548
20587
|
];
|
|
20549
|
-
async function openMemoryDatabase(databasePath, options
|
|
20588
|
+
async function openMemoryDatabase(databasePath, options) {
|
|
20550
20589
|
let database;
|
|
20551
20590
|
try {
|
|
20552
20591
|
mkdirSync2(dirname(databasePath), { recursive: true });
|
|
20553
20592
|
database = new Database(databasePath);
|
|
20554
|
-
await initializeMemoryDatabase(database, createMemoryMigrations(options
|
|
20593
|
+
await initializeMemoryDatabase(database, createMemoryMigrations(options));
|
|
20555
20594
|
return database;
|
|
20556
20595
|
} catch (error2) {
|
|
20557
20596
|
database?.close();
|
|
@@ -20563,7 +20602,7 @@ async function openMemoryDatabase(databasePath, options = {}) {
|
|
|
20563
20602
|
});
|
|
20564
20603
|
}
|
|
20565
20604
|
}
|
|
20566
|
-
async function initializeMemoryDatabase(database, migrations
|
|
20605
|
+
async function initializeMemoryDatabase(database, migrations) {
|
|
20567
20606
|
try {
|
|
20568
20607
|
applyPragmas(database);
|
|
20569
20608
|
await runSqliteMigrations(database, migrations);
|
|
@@ -24195,12 +24234,59 @@ function startWebServer(memory, options) {
|
|
|
24195
24234
|
return serve({ fetch: app.fetch, port: options.port });
|
|
24196
24235
|
}
|
|
24197
24236
|
|
|
24237
|
+
// src/workspace-resolver.ts
|
|
24238
|
+
import { execFile } from "node:child_process";
|
|
24239
|
+
import { basename, dirname as dirname2 } from "node:path";
|
|
24240
|
+
import { promisify } from "node:util";
|
|
24241
|
+
var execFileAsync = promisify(execFile);
|
|
24242
|
+
function createGitWorkspaceResolver(options = {}) {
|
|
24243
|
+
const getGitCommonDir = options.getGitCommonDir ?? defaultGetGitCommonDir;
|
|
24244
|
+
const cache = new Map;
|
|
24245
|
+
return {
|
|
24246
|
+
async resolve(workspace) {
|
|
24247
|
+
const trimmed = normalizeOptionalString(workspace);
|
|
24248
|
+
if (!trimmed) {
|
|
24249
|
+
return;
|
|
24250
|
+
}
|
|
24251
|
+
const cached2 = cache.get(trimmed);
|
|
24252
|
+
if (cached2) {
|
|
24253
|
+
return cached2;
|
|
24254
|
+
}
|
|
24255
|
+
const pending = resolveWorkspace(trimmed, getGitCommonDir);
|
|
24256
|
+
cache.set(trimmed, pending);
|
|
24257
|
+
return pending;
|
|
24258
|
+
}
|
|
24259
|
+
};
|
|
24260
|
+
}
|
|
24261
|
+
async function resolveWorkspace(workspace, getGitCommonDir) {
|
|
24262
|
+
try {
|
|
24263
|
+
const gitCommonDir = (await getGitCommonDir(workspace)).trim();
|
|
24264
|
+
if (basename(gitCommonDir) !== ".git") {
|
|
24265
|
+
return workspace;
|
|
24266
|
+
}
|
|
24267
|
+
return dirname2(gitCommonDir);
|
|
24268
|
+
} catch {
|
|
24269
|
+
return workspace;
|
|
24270
|
+
}
|
|
24271
|
+
}
|
|
24272
|
+
async function defaultGetGitCommonDir(cwd) {
|
|
24273
|
+
const { stdout } = await execFileAsync("git", ["rev-parse", "--path-format=absolute", "--git-common-dir"], {
|
|
24274
|
+
cwd
|
|
24275
|
+
});
|
|
24276
|
+
return stdout.trim();
|
|
24277
|
+
}
|
|
24278
|
+
function normalizeOptionalString(value) {
|
|
24279
|
+
const trimmed = value?.trim();
|
|
24280
|
+
return trimmed ? trimmed : undefined;
|
|
24281
|
+
}
|
|
24282
|
+
|
|
24198
24283
|
// src/index.ts
|
|
24199
24284
|
var config2 = resolveConfig();
|
|
24200
24285
|
var embeddingService = new EmbeddingService({ modelsCachePath: config2.modelsCachePath });
|
|
24201
|
-
var
|
|
24286
|
+
var workspaceResolver = createGitWorkspaceResolver();
|
|
24287
|
+
var database = await openMemoryDatabase(config2.databasePath, { embeddingService, workspaceResolver });
|
|
24202
24288
|
var repository2 = new SqliteMemoryRepository(database);
|
|
24203
|
-
var memoryService = new MemoryService(repository2, embeddingService);
|
|
24289
|
+
var memoryService = new MemoryService(repository2, embeddingService, workspaceResolver);
|
|
24204
24290
|
if (config2.uiMode) {
|
|
24205
24291
|
let shutdown = function() {
|
|
24206
24292
|
server.close();
|