code-session-memory 0.3.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 +290 -0
- package/dist/mcp/index.d.ts +15 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +140 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/server.d.ts +60 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +192 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/src/chunker.d.ts +18 -0
- package/dist/src/chunker.d.ts.map +1 -0
- package/dist/src/chunker.js +149 -0
- package/dist/src/chunker.js.map +1 -0
- package/dist/src/cli.d.ts +11 -0
- package/dist/src/cli.d.ts.map +1 -0
- package/dist/src/cli.js +514 -0
- package/dist/src/cli.js.map +1 -0
- package/dist/src/database.d.ts +50 -0
- package/dist/src/database.d.ts.map +1 -0
- package/dist/src/database.js +238 -0
- package/dist/src/database.js.map +1 -0
- package/dist/src/embedder.d.ts +19 -0
- package/dist/src/embedder.d.ts.map +1 -0
- package/dist/src/embedder.js +80 -0
- package/dist/src/embedder.js.map +1 -0
- package/dist/src/indexer-cli-claude.d.ts +14 -0
- package/dist/src/indexer-cli-claude.d.ts.map +1 -0
- package/dist/src/indexer-cli-claude.js +67 -0
- package/dist/src/indexer-cli-claude.js.map +1 -0
- package/dist/src/indexer-cli.d.ts +16 -0
- package/dist/src/indexer-cli.d.ts.map +1 -0
- package/dist/src/indexer-cli.js +53 -0
- package/dist/src/indexer-cli.js.map +1 -0
- package/dist/src/indexer.d.ts +44 -0
- package/dist/src/indexer.d.ts.map +1 -0
- package/dist/src/indexer.js +129 -0
- package/dist/src/indexer.js.map +1 -0
- package/dist/src/session-to-md.d.ts +15 -0
- package/dist/src/session-to-md.d.ts.map +1 -0
- package/dist/src/session-to-md.js +148 -0
- package/dist/src/session-to-md.js.map +1 -0
- package/dist/src/transcript-to-messages.d.ts +32 -0
- package/dist/src/transcript-to-messages.d.ts.map +1 -0
- package/dist/src/transcript-to-messages.js +230 -0
- package/dist/src/transcript-to-messages.js.map +1 -0
- package/dist/src/types.d.ts +91 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +3 -0
- package/dist/src/types.js.map +1 -0
- package/package.json +59 -0
- package/plugin/memory.ts +42 -0
- package/skill/memory.md +61 -0
package/README.md
ADDED
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
# code-session-memory
|
|
2
|
+
|
|
3
|
+
Automatic vector memory for [OpenCode](https://opencode.ai) and [Claude Code](https://claude.ai/code) sessions — shared across both tools.
|
|
4
|
+
|
|
5
|
+
Every time the AI agent finishes its turn, `code-session-memory` automatically indexes the new messages into a local [sqlite-vec](https://github.com/asg017/sqlite-vec) vector database. Past sessions become semantically searchable — both by the AI agent (via the MCP server) and by you. Sessions from both OpenCode and Claude Code are stored in the **same database**, so memory is shared across tools.
|
|
6
|
+
|
|
7
|
+
## How it works
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
OpenCode (session.idle event) Claude Code (Stop hook)
|
|
11
|
+
│ │
|
|
12
|
+
│ fetches messages via REST API │ reads JSONL transcript
|
|
13
|
+
▼ ▼
|
|
14
|
+
session-to-md transcript-to-messages
|
|
15
|
+
│ │
|
|
16
|
+
└───────────────┬───────────────────────┘
|
|
17
|
+
▼
|
|
18
|
+
chunker → heading-aware chunks (≤1000 tokens, 10% overlap)
|
|
19
|
+
│
|
|
20
|
+
▼
|
|
21
|
+
embedder → OpenAI text-embedding-3-large (3072 dims)
|
|
22
|
+
│
|
|
23
|
+
▼
|
|
24
|
+
sqlite-vec DB → ~/.local/share/code-session-memory/sessions.db
|
|
25
|
+
│
|
|
26
|
+
▼
|
|
27
|
+
MCP server → query_sessions / get_session_chunks tools
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Only **new messages** are indexed on each turn — previously indexed messages are skipped (tracked via `sessions_meta` table). This makes each indexing pass fast, even in long sessions.
|
|
31
|
+
|
|
32
|
+
## Installation
|
|
33
|
+
|
|
34
|
+
### Prerequisites
|
|
35
|
+
|
|
36
|
+
- Node.js ≥ 18
|
|
37
|
+
- An OpenAI API key (for `text-embedding-3-large`)
|
|
38
|
+
- OpenCode and/or Claude Code installed
|
|
39
|
+
|
|
40
|
+
### Install
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
npx code-session-memory install
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
The `install` command sets up everything for both tools in one shot:
|
|
47
|
+
|
|
48
|
+
**OpenCode:**
|
|
49
|
+
1. Copies the plugin to `~/.config/opencode/plugins/code-session-memory.ts`
|
|
50
|
+
2. Copies the skill to `~/.config/opencode/skills/code-session-memory.md`
|
|
51
|
+
3. Writes the MCP server entry into `~/.config/opencode/opencode.json`
|
|
52
|
+
|
|
53
|
+
**Claude Code:**
|
|
54
|
+
1. Writes a `Stop` hook to `~/.claude/settings.json` (fires after each agent turn)
|
|
55
|
+
2. Injects the skill into `~/.claude/CLAUDE.md` (with idempotent markers)
|
|
56
|
+
3. Registers the MCP server via `claude mcp add`
|
|
57
|
+
|
|
58
|
+
**Both tools share:**
|
|
59
|
+
- The same database at `~/.local/share/code-session-memory/sessions.db`
|
|
60
|
+
- The same MCP server for querying past sessions
|
|
61
|
+
|
|
62
|
+
Then **restart OpenCode / Claude Code** to activate.
|
|
63
|
+
|
|
64
|
+
### Set your API key
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
export OPENAI_API_KEY=sk-...
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Add this to your shell profile (`.bashrc`, `.zshrc`, etc.) so it's always available.
|
|
71
|
+
|
|
72
|
+
## Usage
|
|
73
|
+
|
|
74
|
+
Once installed, memory indexing is **fully automatic**. No further action needed — sessions are indexed as you use OpenCode or Claude Code.
|
|
75
|
+
|
|
76
|
+
### Verify installation
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
npx code-session-memory status
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Output:
|
|
83
|
+
```
|
|
84
|
+
code-session-memory status
|
|
85
|
+
|
|
86
|
+
Database ~/.local/share/code-session-memory/sessions.db ✓
|
|
87
|
+
Indexed chunks: 1842
|
|
88
|
+
Sessions tracked: 47
|
|
89
|
+
|
|
90
|
+
OpenCode
|
|
91
|
+
Plugin ~/.config/opencode/plugins/code-session-memory.ts ✓
|
|
92
|
+
Skill ~/.config/opencode/skills/code-session-memory.md ✓
|
|
93
|
+
MCP server /path/to/dist/mcp/index.js ✓
|
|
94
|
+
MCP config ~/.config/opencode/opencode.json ✓
|
|
95
|
+
|
|
96
|
+
Claude Code
|
|
97
|
+
Stop hook ~/.claude/settings.json ✓
|
|
98
|
+
Skill ~/.claude/CLAUDE.md ✓
|
|
99
|
+
MCP server /path/to/dist/mcp/index.js ✓
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### MCP tools
|
|
103
|
+
|
|
104
|
+
The agent can use two MCP tools (and will automatically via the installed skill):
|
|
105
|
+
|
|
106
|
+
#### `query_sessions`
|
|
107
|
+
|
|
108
|
+
Semantic search across all indexed sessions.
|
|
109
|
+
|
|
110
|
+
| Parameter | Type | Required | Description |
|
|
111
|
+
|---|---|---|---|
|
|
112
|
+
| `queryText` | string | yes | Natural language query |
|
|
113
|
+
| `project` | string | no | Filter by project directory path |
|
|
114
|
+
| `source` | string | no | Filter by tool: `"opencode"` or `"claude-code"` |
|
|
115
|
+
| `limit` | number | no | Max results (default: 5) |
|
|
116
|
+
|
|
117
|
+
Example result:
|
|
118
|
+
```
|
|
119
|
+
Result 1:
|
|
120
|
+
Content: [Session: Add dark mode toggle > User]
|
|
121
|
+
|
|
122
|
+
How can I implement a dark mode toggle using CSS variables?
|
|
123
|
+
Distance: 0.1823
|
|
124
|
+
URL: session://ses_abc123#msg_def456
|
|
125
|
+
Section: User
|
|
126
|
+
Chunk: 1 of 1
|
|
127
|
+
---
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
#### `get_session_chunks`
|
|
131
|
+
|
|
132
|
+
Retrieve the full ordered content of a specific session message. Use the `session://ses_xxx#msg_yyy` URL from `query_sessions` results.
|
|
133
|
+
|
|
134
|
+
| Parameter | Type | Required | Description |
|
|
135
|
+
|---|---|---|---|
|
|
136
|
+
| `sessionUrl` | string | yes | URL from `query_sessions` result |
|
|
137
|
+
| `startIndex` | number | no | First chunk index (0-based) |
|
|
138
|
+
| `endIndex` | number | no | Last chunk index (0-based, inclusive) |
|
|
139
|
+
|
|
140
|
+
### Asking the agent about past sessions
|
|
141
|
+
|
|
142
|
+
The installed skill teaches the agent when and how to use these tools. Example prompts:
|
|
143
|
+
|
|
144
|
+
```
|
|
145
|
+
How did we implement the authentication middleware last week?
|
|
146
|
+
Have we discussed this error before?
|
|
147
|
+
What was our decision about the database schema?
|
|
148
|
+
Show me how we solved the TypeScript config issue.
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Configuration
|
|
152
|
+
|
|
153
|
+
### Environment variables
|
|
154
|
+
|
|
155
|
+
| Variable | Default | Description |
|
|
156
|
+
|---|---|---|
|
|
157
|
+
| `OPENAI_API_KEY` | — | **Required.** Used for embedding generation. |
|
|
158
|
+
| `OPENCODE_MEMORY_DB_PATH` | `~/.local/share/code-session-memory/sessions.db` | Override the database path. |
|
|
159
|
+
| `OPENCODE_CONFIG_DIR` | `~/.config/opencode` | Override the OpenCode config directory. |
|
|
160
|
+
| `OPENAI_MODEL` | `text-embedding-3-large` | Override the embedding model. |
|
|
161
|
+
|
|
162
|
+
### Database path
|
|
163
|
+
|
|
164
|
+
The default database path works on both **macOS** and **Linux**. On macOS, `~/.local/share` is used for cross-platform consistency (rather than `~/Library/Application Support`).
|
|
165
|
+
|
|
166
|
+
To change it:
|
|
167
|
+
```bash
|
|
168
|
+
export OPENCODE_MEMORY_DB_PATH=/custom/path/sessions.db
|
|
169
|
+
npx code-session-memory install
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## Project structure
|
|
173
|
+
|
|
174
|
+
```
|
|
175
|
+
code-session-memory/
|
|
176
|
+
├── src/
|
|
177
|
+
│ ├── types.ts # Shared TypeScript types
|
|
178
|
+
│ ├── database.ts # SQLite-vec: init, insert, query
|
|
179
|
+
│ ├── chunker.ts # Heading-aware markdown chunker
|
|
180
|
+
│ ├── embedder.ts # OpenAI embeddings (batched)
|
|
181
|
+
│ ├── session-to-md.ts # OpenCode SDK messages → markdown
|
|
182
|
+
│ ├── transcript-to-messages.ts # Claude Code JSONL transcript parser
|
|
183
|
+
│ ├── indexer.ts # Orchestrator: incremental indexing
|
|
184
|
+
│ ├── indexer-cli.ts # Node.js subprocess (called by OpenCode plugin)
|
|
185
|
+
│ ├── indexer-cli-claude.ts # Node.js subprocess (called by Claude Code hook)
|
|
186
|
+
│ └── cli.ts # install / status / uninstall commands
|
|
187
|
+
├── mcp/
|
|
188
|
+
│ ├── server.ts # MCP query handlers (testable, injected deps)
|
|
189
|
+
│ └── index.ts # MCP stdio server entry point
|
|
190
|
+
├── plugin/
|
|
191
|
+
│ └── memory.ts # OpenCode plugin (session.idle hook)
|
|
192
|
+
├── skill/
|
|
193
|
+
│ └── memory.md # Skill instructions (injected into both tools)
|
|
194
|
+
└── tests/
|
|
195
|
+
├── chunker.test.ts
|
|
196
|
+
├── database.test.ts
|
|
197
|
+
├── embedder.test.ts
|
|
198
|
+
├── indexer.test.ts
|
|
199
|
+
├── mcp-server.test.ts
|
|
200
|
+
└── session-to-md.test.ts
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
## Development
|
|
204
|
+
|
|
205
|
+
```bash
|
|
206
|
+
# Install dependencies
|
|
207
|
+
npm install
|
|
208
|
+
|
|
209
|
+
# Build
|
|
210
|
+
npm run build
|
|
211
|
+
|
|
212
|
+
# Run tests
|
|
213
|
+
npm test
|
|
214
|
+
|
|
215
|
+
# Watch mode
|
|
216
|
+
npm run test:watch
|
|
217
|
+
|
|
218
|
+
# Coverage report
|
|
219
|
+
npm run test:coverage
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### Running tests
|
|
223
|
+
|
|
224
|
+
Tests use [Vitest](https://vitest.dev) and run without any external dependencies:
|
|
225
|
+
- No real OpenAI API calls — the embedder is mocked
|
|
226
|
+
- No real DB files — SQLite uses in-memory databases (`:memory:`) for unit tests
|
|
227
|
+
- Indexer tests use a temp directory on disk
|
|
228
|
+
|
|
229
|
+
```
|
|
230
|
+
✓ tests/chunker.test.ts (15 tests)
|
|
231
|
+
✓ tests/mcp-server.test.ts (13 tests)
|
|
232
|
+
✓ tests/session-to-md.test.ts (18 tests)
|
|
233
|
+
✓ tests/embedder.test.ts (9 tests)
|
|
234
|
+
✓ tests/database.test.ts (25 tests)
|
|
235
|
+
✓ tests/indexer.test.ts (8 tests)
|
|
236
|
+
Tests 88 passed
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
## Uninstall
|
|
240
|
+
|
|
241
|
+
```bash
|
|
242
|
+
npx code-session-memory uninstall
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
This removes the plugin, hooks, and skill files for both tools. The database is **not** removed automatically.
|
|
246
|
+
|
|
247
|
+
To also remove the database:
|
|
248
|
+
```bash
|
|
249
|
+
rm ~/.local/share/code-session-memory/sessions.db
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
## Architecture notes
|
|
253
|
+
|
|
254
|
+
### Incremental indexing
|
|
255
|
+
|
|
256
|
+
The plugin/hook fires on every agent turn. To avoid re-processing the entire session history each time, the indexer:
|
|
257
|
+
|
|
258
|
+
1. Reads `last_indexed_message_id` from the `sessions_meta` table
|
|
259
|
+
2. Skips all messages up to and including that ID
|
|
260
|
+
3. Processes only the new messages
|
|
261
|
+
4. Updates `last_indexed_message_id` after success
|
|
262
|
+
|
|
263
|
+
This makes each indexing pass O(new messages) rather than O(all messages).
|
|
264
|
+
|
|
265
|
+
### Why a Node.js subprocess?
|
|
266
|
+
|
|
267
|
+
OpenCode plugins run inside **Bun**, but `better-sqlite3` and `sqlite-vec` are native Node.js addons that don't load under Bun. The plugin therefore spawns a Node.js subprocess (`indexer-cli.js`) to handle all database operations. The Claude Code hook calls a similar subprocess (`indexer-cli-claude.js`) which reads the transcript JSONL from disk.
|
|
268
|
+
|
|
269
|
+
### Claude Code transcript parsing
|
|
270
|
+
|
|
271
|
+
Claude Code writes a JSONL transcript after each session turn. The parser (`transcript-to-messages.ts`) handles:
|
|
272
|
+
- Deduplicating streaming `assistant` chunks (keeps the last entry per `message.id`)
|
|
273
|
+
- Skipping internal `thinking` blocks, metadata entries, and error messages
|
|
274
|
+
- Converting `tool_use` / `tool_result` entries to readable markdown
|
|
275
|
+
|
|
276
|
+
### Chunking strategy
|
|
277
|
+
|
|
278
|
+
- Heading-aware splitting — headings define semantic boundaries
|
|
279
|
+
- Max 1000 whitespace-tokenized words per chunk
|
|
280
|
+
- Sections below 150 words are merged with adjacent sections
|
|
281
|
+
- Sections above 1000 words are split with 10% overlap
|
|
282
|
+
- Each chunk gets a `[Session: Title > Section]` breadcrumb prefix injected before embedding, improving retrieval precision
|
|
283
|
+
|
|
284
|
+
### MCP server
|
|
285
|
+
|
|
286
|
+
The MCP server uses **stdio transport** — the simplest and most reliable transport for local use. It opens and closes the SQLite connection on each query (no persistent connection), making it safe to run alongside the indexer. The `query_sessions` tool accepts an optional `source` parameter to filter results to a specific tool.
|
|
287
|
+
|
|
288
|
+
## License
|
|
289
|
+
|
|
290
|
+
MIT
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* code-session-memory MCP server (stdio transport).
|
|
4
|
+
*
|
|
5
|
+
* Exposes two tools:
|
|
6
|
+
* - query_sessions — semantic search across indexed sessions (OpenCode + Claude Code)
|
|
7
|
+
* - get_session_chunks — retrieve ordered chunks for a specific session URL
|
|
8
|
+
*
|
|
9
|
+
* Environment variables:
|
|
10
|
+
* OPENAI_API_KEY — required for embedding generation
|
|
11
|
+
* OPENAI_MODEL — embedding model (default: text-embedding-3-large)
|
|
12
|
+
* OPENCODE_MEMORY_DB_PATH — path to the sqlite-vec DB
|
|
13
|
+
*/
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../mcp/index.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;GAWG"}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
/**
|
|
4
|
+
* code-session-memory MCP server (stdio transport).
|
|
5
|
+
*
|
|
6
|
+
* Exposes two tools:
|
|
7
|
+
* - query_sessions — semantic search across indexed sessions (OpenCode + Claude Code)
|
|
8
|
+
* - get_session_chunks — retrieve ordered chunks for a specific session URL
|
|
9
|
+
*
|
|
10
|
+
* Environment variables:
|
|
11
|
+
* OPENAI_API_KEY — required for embedding generation
|
|
12
|
+
* OPENAI_MODEL — embedding model (default: text-embedding-3-large)
|
|
13
|
+
* OPENCODE_MEMORY_DB_PATH — path to the sqlite-vec DB
|
|
14
|
+
*/
|
|
15
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
16
|
+
if (k2 === undefined) k2 = k;
|
|
17
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
18
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
19
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
20
|
+
}
|
|
21
|
+
Object.defineProperty(o, k2, desc);
|
|
22
|
+
}) : (function(o, m, k, k2) {
|
|
23
|
+
if (k2 === undefined) k2 = k;
|
|
24
|
+
o[k2] = m[k];
|
|
25
|
+
}));
|
|
26
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
27
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
28
|
+
}) : function(o, v) {
|
|
29
|
+
o["default"] = v;
|
|
30
|
+
});
|
|
31
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
32
|
+
var ownKeys = function(o) {
|
|
33
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
34
|
+
var ar = [];
|
|
35
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
36
|
+
return ar;
|
|
37
|
+
};
|
|
38
|
+
return ownKeys(o);
|
|
39
|
+
};
|
|
40
|
+
return function (mod) {
|
|
41
|
+
if (mod && mod.__esModule) return mod;
|
|
42
|
+
var result = {};
|
|
43
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
44
|
+
__setModuleDefault(result, mod);
|
|
45
|
+
return result;
|
|
46
|
+
};
|
|
47
|
+
})();
|
|
48
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
49
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
50
|
+
};
|
|
51
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
52
|
+
const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
53
|
+
const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
54
|
+
const zod_1 = require("zod");
|
|
55
|
+
const openai_1 = require("openai");
|
|
56
|
+
const sqliteVec = __importStar(require("sqlite-vec"));
|
|
57
|
+
const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
|
|
58
|
+
const fs_1 = __importDefault(require("fs"));
|
|
59
|
+
const database_1 = require("../src/database");
|
|
60
|
+
const server_1 = require("./server");
|
|
61
|
+
// ---------------------------------------------------------------------------
|
|
62
|
+
// Configuration
|
|
63
|
+
// ---------------------------------------------------------------------------
|
|
64
|
+
const dbPath = (0, database_1.resolveDbPath)();
|
|
65
|
+
const openAiApiKey = process.env.OPENAI_API_KEY;
|
|
66
|
+
const openAiModel = process.env.OPENAI_MODEL ?? "text-embedding-3-large";
|
|
67
|
+
if (!openAiApiKey) {
|
|
68
|
+
console.error("Error: OPENAI_API_KEY environment variable is required.");
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
if (!fs_1.default.existsSync(dbPath)) {
|
|
72
|
+
console.error(`Error: Database not found at ${dbPath}.\nRun "npx code-session-memory install" first.`);
|
|
73
|
+
process.exit(1);
|
|
74
|
+
}
|
|
75
|
+
// ---------------------------------------------------------------------------
|
|
76
|
+
// Embedding function
|
|
77
|
+
// ---------------------------------------------------------------------------
|
|
78
|
+
const openai = new openai_1.OpenAI({ apiKey: openAiApiKey });
|
|
79
|
+
async function createEmbedding(text) {
|
|
80
|
+
const MAX_CHARS = 8191 * 4;
|
|
81
|
+
const input = text.length > MAX_CHARS ? text.slice(0, MAX_CHARS) : text;
|
|
82
|
+
const response = await openai.embeddings.create({ model: openAiModel, input });
|
|
83
|
+
const embedding = response.data?.[0]?.embedding;
|
|
84
|
+
if (!embedding)
|
|
85
|
+
throw new Error("No embedding returned from OpenAI API");
|
|
86
|
+
return embedding;
|
|
87
|
+
}
|
|
88
|
+
// ---------------------------------------------------------------------------
|
|
89
|
+
// SQLite provider + tool handlers
|
|
90
|
+
// ---------------------------------------------------------------------------
|
|
91
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
92
|
+
const provider = (0, server_1.createSqliteProvider)({
|
|
93
|
+
dbPath,
|
|
94
|
+
sqliteVec: sqliteVec,
|
|
95
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
96
|
+
Database: better_sqlite3_1.default,
|
|
97
|
+
fs: fs_1.default,
|
|
98
|
+
});
|
|
99
|
+
const { querySessionsHandler, getSessionChunksHandler } = (0, server_1.createToolHandlers)({
|
|
100
|
+
createEmbedding,
|
|
101
|
+
querySessions: provider.querySessions,
|
|
102
|
+
getSessionChunks: provider.getSessionChunks,
|
|
103
|
+
});
|
|
104
|
+
// ---------------------------------------------------------------------------
|
|
105
|
+
// MCP server
|
|
106
|
+
// ---------------------------------------------------------------------------
|
|
107
|
+
const server = new mcp_js_1.McpServer({
|
|
108
|
+
name: "code-session-memory",
|
|
109
|
+
version: "0.3.0",
|
|
110
|
+
});
|
|
111
|
+
// Zod schemas defined separately to avoid type instantiation depth issues
|
|
112
|
+
const querySessionsSchema = {
|
|
113
|
+
queryText: zod_1.z.string().min(1).describe("Natural language description of what you are looking for."),
|
|
114
|
+
project: zod_1.z.string().optional().describe("Filter results to a specific project directory path (e.g. '/Users/me/myproject'). Optional."),
|
|
115
|
+
source: zod_1.z.enum(["opencode", "claude-code"]).optional().describe("Filter results by tool source: 'opencode' or 'claude-code'. Optional — omit to search across both."),
|
|
116
|
+
limit: zod_1.z.number().int().min(1).optional().describe("Maximum number of results to return. Defaults to 5."),
|
|
117
|
+
};
|
|
118
|
+
const getSessionChunksSchema = {
|
|
119
|
+
sessionUrl: zod_1.z.string().min(1).describe("The session message URL from a query_sessions result (e.g. 'session://ses_xxx#msg_yyy')."),
|
|
120
|
+
startIndex: zod_1.z.number().int().min(0).optional().describe("First chunk index to retrieve (0-based, inclusive). Optional."),
|
|
121
|
+
endIndex: zod_1.z.number().int().min(0).optional().describe("Last chunk index to retrieve (0-based, inclusive). Optional."),
|
|
122
|
+
};
|
|
123
|
+
// Cast server to any to avoid deep MCP SDK Zod type instantiation (TS2589)
|
|
124
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
125
|
+
const serverAny = server;
|
|
126
|
+
serverAny.tool("query_sessions", "Semantically search across all indexed sessions stored in the vector database. Returns the most relevant chunks from past sessions. Sessions from both OpenCode and Claude Code are indexed into the same shared database.", querySessionsSchema, async (args) => querySessionsHandler({ ...args, limit: args.limit ?? 5 }));
|
|
127
|
+
serverAny.tool("get_session_chunks", "Retrieve the ordered content chunks for a specific session message. Use the URL from query_sessions results (e.g. 'session://ses_xxx#msg_yyy') to get the full context around a match.", getSessionChunksSchema, async (args) => getSessionChunksHandler(args));
|
|
128
|
+
// ---------------------------------------------------------------------------
|
|
129
|
+
// Start
|
|
130
|
+
// ---------------------------------------------------------------------------
|
|
131
|
+
async function main() {
|
|
132
|
+
const transport = new stdio_js_1.StdioServerTransport();
|
|
133
|
+
await server.connect(transport);
|
|
134
|
+
console.error(`code-session-memory MCP server running (DB: ${dbPath})`);
|
|
135
|
+
}
|
|
136
|
+
main().catch((err) => {
|
|
137
|
+
console.error("Failed to start MCP server:", err);
|
|
138
|
+
process.exit(1);
|
|
139
|
+
});
|
|
140
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../mcp/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP query handlers for opencode-memory.
|
|
3
|
+
*
|
|
4
|
+
* Adapted from doc2vec/mcp/src/server.ts — simplified to:
|
|
5
|
+
* - SQLite-vec only (no Qdrant)
|
|
6
|
+
* - Fixed single DB path (no multi-DB resolution)
|
|
7
|
+
* - Two tools: query_sessions + get_session_chunks
|
|
8
|
+
*/
|
|
9
|
+
import type { QueryResult } from "../src/types";
|
|
10
|
+
export type { QueryResult };
|
|
11
|
+
type SqliteVecModule = {
|
|
12
|
+
load: (db: SqliteDatabase) => void;
|
|
13
|
+
};
|
|
14
|
+
type SqliteDatabase = {
|
|
15
|
+
prepare: (sql: string) => SqliteStatement;
|
|
16
|
+
close: () => void;
|
|
17
|
+
};
|
|
18
|
+
type SqliteStatement = {
|
|
19
|
+
all: (...params: unknown[]) => unknown[];
|
|
20
|
+
};
|
|
21
|
+
type FsModule = {
|
|
22
|
+
existsSync: (p: string) => boolean;
|
|
23
|
+
};
|
|
24
|
+
export declare function createSqliteProvider(deps: {
|
|
25
|
+
dbPath: string;
|
|
26
|
+
sqliteVec: SqliteVecModule;
|
|
27
|
+
Database: new (path: string) => SqliteDatabase;
|
|
28
|
+
fs: FsModule;
|
|
29
|
+
}): {
|
|
30
|
+
querySessions: (queryEmbedding: number[], topK?: number, projectFilter?: string, sourceFilter?: string) => Promise<QueryResult[]>;
|
|
31
|
+
getSessionChunks: (url: string, startIndex?: number, endIndex?: number) => Promise<QueryResult[]>;
|
|
32
|
+
};
|
|
33
|
+
export declare function createToolHandlers(deps: {
|
|
34
|
+
createEmbedding: (text: string) => Promise<number[]>;
|
|
35
|
+
querySessions: (embedding: number[], topK: number, project?: string, source?: string) => Promise<QueryResult[]>;
|
|
36
|
+
getSessionChunks: (url: string, startIndex?: number, endIndex?: number) => Promise<QueryResult[]>;
|
|
37
|
+
}): {
|
|
38
|
+
querySessionsHandler: (args: {
|
|
39
|
+
queryText: string;
|
|
40
|
+
project?: string;
|
|
41
|
+
source?: string;
|
|
42
|
+
limit?: number;
|
|
43
|
+
}) => Promise<{
|
|
44
|
+
content: {
|
|
45
|
+
type: "text";
|
|
46
|
+
text: string;
|
|
47
|
+
}[];
|
|
48
|
+
}>;
|
|
49
|
+
getSessionChunksHandler: (args: {
|
|
50
|
+
sessionUrl: string;
|
|
51
|
+
startIndex?: number;
|
|
52
|
+
endIndex?: number;
|
|
53
|
+
}) => Promise<{
|
|
54
|
+
content: {
|
|
55
|
+
type: "text";
|
|
56
|
+
text: string;
|
|
57
|
+
}[];
|
|
58
|
+
}>;
|
|
59
|
+
};
|
|
60
|
+
//# sourceMappingURL=server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../mcp/server.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAKhD,YAAY,EAAE,WAAW,EAAE,CAAC;AAM5B,KAAK,eAAe,GAAG;IAAE,IAAI,EAAE,CAAC,EAAE,EAAE,cAAc,KAAK,IAAI,CAAA;CAAE,CAAC;AAC9D,KAAK,cAAc,GAAG;IACpB,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,eAAe,CAAC;IAC1C,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB,CAAC;AACF,KAAK,eAAe,GAAG;IACrB,GAAG,EAAE,CAAC,GAAG,MAAM,EAAE,OAAO,EAAE,KAAK,OAAO,EAAE,CAAC;CAC1C,CAAC;AACF,KAAK,QAAQ,GAAG;IAAE,UAAU,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,OAAO,CAAA;CAAE,CAAC;AAMvD,wBAAgB,oBAAoB,CAAC,IAAI,EAAE;IACzC,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,eAAe,CAAC;IAC3B,QAAQ,EAAE,KAAK,IAAI,EAAE,MAAM,KAAK,cAAc,CAAC;IAC/C,EAAE,EAAE,QAAQ,CAAC;CACd;oCAsBmB,MAAM,EAAE,iCAER,MAAM,iBACP,MAAM,KACpB,OAAO,CAAC,WAAW,EAAE,CAAC;4BA0ClB,MAAM,eACE,MAAM,aACR,MAAM,KAChB,OAAO,CAAC,WAAW,EAAE,CAAC;EAwB1B;AAMD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE;IACvC,eAAe,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IACrD,aAAa,EAAE,CACb,SAAS,EAAE,MAAM,EAAE,EACnB,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,MAAM,EAChB,MAAM,CAAC,EAAE,MAAM,KACZ,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;IAC5B,gBAAgB,EAAE,CAChB,GAAG,EAAE,MAAM,EACX,UAAU,CAAC,EAAE,MAAM,EACnB,QAAQ,CAAC,EAAE,MAAM,KACd,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;CAC7B;iCAK2C;QACxC,SAAS,EAAE,MAAM,CAAC;QAClB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB;;;;;;oCAuD4C;QAC3C,UAAU,EAAE,MAAM,CAAC;QACnB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB;;;;;;EAwDF"}
|