@strvmarv/total-recall 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/.claude-plugin/plugin.json +18 -0
- package/.copilot-plugin/plugin.json +12 -0
- package/.cursor-plugin/plugin.json +13 -0
- package/.opencode/INSTALL.md +24 -0
- package/CONTRIBUTING.md +295 -0
- package/LICENSE +21 -0
- package/README.md +239 -0
- package/agents/compactor.md +47 -0
- package/dist/index.js +2554 -0
- package/eval/benchmarks/retrieval.jsonl +20 -0
- package/eval/corpus/memories.jsonl +20 -0
- package/hooks/hooks-cursor.json +16 -0
- package/hooks/hooks.json +16 -0
- package/hooks/session-end/run.sh +5 -0
- package/hooks/session-start/run.sh +11 -0
- package/package.json +78 -0
- package/skills/forget/SKILL.md +23 -0
- package/skills/ingest/SKILL.md +20 -0
- package/skills/memory/SKILL.md +60 -0
- package/skills/search/SKILL.md +19 -0
- package/skills/status/SKILL.md +32 -0
- package/src/defaults.toml +28 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "total-recall",
|
|
3
|
+
"description": "Multi-tiered memory and knowledge base with semantic search, auto-compaction, and built-in evaluation. Works across Claude Code, Copilot CLI, OpenCode, Cline, and Cursor.",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "strvmarv"
|
|
7
|
+
},
|
|
8
|
+
"skills": "./skills/",
|
|
9
|
+
"agents": "./agents/",
|
|
10
|
+
"hooks": "./hooks/hooks.json",
|
|
11
|
+
"mcpServers": {
|
|
12
|
+
"total-recall": {
|
|
13
|
+
"command": "node",
|
|
14
|
+
"args": ["dist/index.js"],
|
|
15
|
+
"env": {}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "total-recall",
|
|
3
|
+
"description": "Multi-tiered memory and knowledge base for TUI coding assistants",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"skills": "./skills/",
|
|
6
|
+
"mcpServers": {
|
|
7
|
+
"total-recall": {
|
|
8
|
+
"command": "node",
|
|
9
|
+
"args": ["dist/index.js"]
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "total-recall",
|
|
3
|
+
"description": "Multi-tiered memory and knowledge base for TUI coding assistants",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"skills": "./skills/",
|
|
6
|
+
"hooks": "./hooks/hooks-cursor.json",
|
|
7
|
+
"mcpServers": {
|
|
8
|
+
"total-recall": {
|
|
9
|
+
"command": "node",
|
|
10
|
+
"args": ["dist/index.js"]
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# total-recall for OpenCode
|
|
2
|
+
|
|
3
|
+
## Installation
|
|
4
|
+
|
|
5
|
+
1. Add the MCP server to your OpenCode config:
|
|
6
|
+
|
|
7
|
+
```json
|
|
8
|
+
{
|
|
9
|
+
"mcpServers": {
|
|
10
|
+
"total-recall": {
|
|
11
|
+
"command": "node",
|
|
12
|
+
"args": ["/path/to/total-recall/dist/index.js"]
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
2. Copy the skills directory to your OpenCode plugins:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
cp -r skills/ ~/.opencode/plugins/total-recall/skills/
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
3. Restart OpenCode. total-recall will auto-initialize on first session.
|
package/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
# Contributing to total-recall
|
|
2
|
+
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
## Getting Started
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
git clone https://github.com/strvmarv/total-recall.git
|
|
9
|
+
cd total-recall
|
|
10
|
+
npm install
|
|
11
|
+
npm run build
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Run tests to verify your environment:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm test
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Run the type checker:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm run typecheck
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Adding a New Host Tool Importer
|
|
29
|
+
|
|
30
|
+
Host importers detect a specific tool's presence, scan its memory files, and migrate them into total-recall on first run.
|
|
31
|
+
|
|
32
|
+
### 1. Implement the `HostImporter` interface
|
|
33
|
+
|
|
34
|
+
The interface is defined in `src/importers/importer.ts`:
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
export interface HostImporter {
|
|
38
|
+
name: string;
|
|
39
|
+
detect(): boolean;
|
|
40
|
+
scan(): { memoryFiles: number; knowledgeFiles: number; sessionFiles: number };
|
|
41
|
+
importMemories(db: Database.Database, embed: EmbedFn, project?: string): ImportResult;
|
|
42
|
+
importKnowledge(db: Database.Database, embed: EmbedFn): ImportResult;
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Create `src/importers/my-tool.ts`:
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
50
|
+
import { join } from "node:path";
|
|
51
|
+
import { homedir } from "node:os";
|
|
52
|
+
import { createHash } from "node:crypto";
|
|
53
|
+
import type Database from "better-sqlite3";
|
|
54
|
+
import type { HostImporter, ImportResult } from "./importer.js";
|
|
55
|
+
import { insertEntry } from "../db/entries.js";
|
|
56
|
+
import { insertEmbedding } from "../search/vector-search.js";
|
|
57
|
+
|
|
58
|
+
type EmbedFn = (text: string) => Float32Array;
|
|
59
|
+
|
|
60
|
+
// Where my-tool stores its memories
|
|
61
|
+
const MY_TOOL_DIR = join(homedir(), ".my-tool");
|
|
62
|
+
|
|
63
|
+
export const myToolImporter: HostImporter = {
|
|
64
|
+
name: "my-tool",
|
|
65
|
+
|
|
66
|
+
detect() {
|
|
67
|
+
return existsSync(MY_TOOL_DIR);
|
|
68
|
+
},
|
|
69
|
+
|
|
70
|
+
scan() {
|
|
71
|
+
if (!existsSync(MY_TOOL_DIR)) {
|
|
72
|
+
return { memoryFiles: 0, knowledgeFiles: 0, sessionFiles: 0 };
|
|
73
|
+
}
|
|
74
|
+
const files = readdirSync(MY_TOOL_DIR).filter((f) => f.endsWith(".md"));
|
|
75
|
+
return { memoryFiles: files.length, knowledgeFiles: 0, sessionFiles: 0 };
|
|
76
|
+
},
|
|
77
|
+
|
|
78
|
+
importMemories(db, embed, project) {
|
|
79
|
+
if (!existsSync(MY_TOOL_DIR)) return { imported: 0, skipped: 0, errors: [] };
|
|
80
|
+
|
|
81
|
+
const result: ImportResult = { imported: 0, skipped: 0, errors: [] };
|
|
82
|
+
|
|
83
|
+
const files = readdirSync(MY_TOOL_DIR).filter((f) => f.endsWith(".md"));
|
|
84
|
+
for (const file of files) {
|
|
85
|
+
const filePath = join(MY_TOOL_DIR, file);
|
|
86
|
+
try {
|
|
87
|
+
const raw = readFileSync(filePath, "utf-8").trim();
|
|
88
|
+
if (!raw) { result.skipped++; continue; }
|
|
89
|
+
|
|
90
|
+
const hash = createHash("sha256").update(raw).digest("hex");
|
|
91
|
+
|
|
92
|
+
// Check if already imported (use import_log table)
|
|
93
|
+
const existing = db
|
|
94
|
+
.prepare("SELECT id FROM import_log WHERE content_hash = ?")
|
|
95
|
+
.get(hash) as { id: string } | undefined;
|
|
96
|
+
if (existing) { result.skipped++; continue; }
|
|
97
|
+
|
|
98
|
+
const id = crypto.randomUUID();
|
|
99
|
+
const embedding = embed(raw);
|
|
100
|
+
|
|
101
|
+
insertEntry(db, {
|
|
102
|
+
id,
|
|
103
|
+
content: raw,
|
|
104
|
+
tier: "warm",
|
|
105
|
+
content_type: "note",
|
|
106
|
+
source_tool: "my-tool",
|
|
107
|
+
source_path: filePath,
|
|
108
|
+
project: project ?? null,
|
|
109
|
+
});
|
|
110
|
+
insertEmbedding(db, id, embedding);
|
|
111
|
+
|
|
112
|
+
// Log the import to avoid duplicates on next run
|
|
113
|
+
db.prepare(
|
|
114
|
+
"INSERT INTO import_log (id, source_tool, source_path, content_hash, entry_id, tier, content_type) VALUES (?, ?, ?, ?, ?, ?, ?)"
|
|
115
|
+
).run(crypto.randomUUID(), "my-tool", filePath, hash, id, "warm", "note");
|
|
116
|
+
|
|
117
|
+
result.imported++;
|
|
118
|
+
} catch (err) {
|
|
119
|
+
result.errors.push(`${file}: ${String(err)}`);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return result;
|
|
124
|
+
},
|
|
125
|
+
|
|
126
|
+
importKnowledge(_db, _embed) {
|
|
127
|
+
// my-tool has no separate knowledge files
|
|
128
|
+
return { imported: 0, skipped: 0, errors: [] };
|
|
129
|
+
},
|
|
130
|
+
};
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### 2. Register the importer
|
|
134
|
+
|
|
135
|
+
Add it to the importer list in `src/index.ts` (or wherever importers are initialized at startup).
|
|
136
|
+
|
|
137
|
+
### 3. Add tests
|
|
138
|
+
|
|
139
|
+
Create `src/importers/my-tool.test.ts` mirroring the existing `claude-code.test.ts` structure.
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## Adding a New Content Type
|
|
144
|
+
|
|
145
|
+
Content types classify what kind of information a memory or knowledge chunk contains (e.g., `note`, `code`, `doc`, `decision`).
|
|
146
|
+
|
|
147
|
+
### 1. Add the type to the schema
|
|
148
|
+
|
|
149
|
+
In `src/db/schema.ts`, find the `content_type` column definition and add your new type to the CHECK constraint or lookup table:
|
|
150
|
+
|
|
151
|
+
```sql
|
|
152
|
+
-- In the entries table or a lookup table:
|
|
153
|
+
content_type TEXT CHECK(content_type IN ('note', 'code', 'doc', 'decision', 'my-new-type'))
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### 2. Add the type to the TypeScript union
|
|
157
|
+
|
|
158
|
+
In `src/types.ts`, extend the `ContentType` union:
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
export type ContentType = "note" | "code" | "doc" | "decision" | "my-new-type";
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### 3. Assign a relevance weight (optional)
|
|
165
|
+
|
|
166
|
+
If your content type should be scored differently during retrieval, add a weight entry in the search relevance config (see `src/search/`).
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## Adding a New Chunking Parser
|
|
171
|
+
|
|
172
|
+
The chunker in `src/ingestion/chunker.ts` dispatches to per-format parsers based on file extension. To add support for a new format:
|
|
173
|
+
|
|
174
|
+
### 1. Implement the parser
|
|
175
|
+
|
|
176
|
+
Create `src/ingestion/my-format-parser.ts`. Your parser must return `Chunk[]`:
|
|
177
|
+
|
|
178
|
+
```typescript
|
|
179
|
+
import type { Chunk } from "./chunker.js";
|
|
180
|
+
|
|
181
|
+
export interface ParseOptions {
|
|
182
|
+
maxTokens: number;
|
|
183
|
+
overlapTokens?: number;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export function parseMyFormat(content: string, opts: ParseOptions): Chunk[] {
|
|
187
|
+
const chunks: Chunk[] = [];
|
|
188
|
+
|
|
189
|
+
// Split your format into logical units.
|
|
190
|
+
// Each chunk needs: content, startLine, endLine.
|
|
191
|
+
// Optionally: headingPath (for outline-like formats), name/kind (for code-like formats).
|
|
192
|
+
|
|
193
|
+
let lineNumber = 1;
|
|
194
|
+
for (const section of splitIntoSections(content)) {
|
|
195
|
+
chunks.push({
|
|
196
|
+
content: section.text,
|
|
197
|
+
startLine: lineNumber,
|
|
198
|
+
endLine: lineNumber + section.text.split("\n").length - 1,
|
|
199
|
+
});
|
|
200
|
+
lineNumber += section.text.split("\n").length + 1;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return chunks;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function splitIntoSections(content: string) {
|
|
207
|
+
// Your format-specific splitting logic here
|
|
208
|
+
return content.split(/\n---\n/).map((text) => ({ text }));
|
|
209
|
+
}
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### 2. Register the parser in `chunker.ts`
|
|
213
|
+
|
|
214
|
+
In `src/ingestion/chunker.ts`, add your file extensions and import:
|
|
215
|
+
|
|
216
|
+
```typescript
|
|
217
|
+
import { parseMyFormat } from "./my-format-parser.js";
|
|
218
|
+
|
|
219
|
+
const MY_FORMAT_EXTENSIONS = new Set([".myext", ".myfmt"]);
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
Then add a branch in the `chunkFile` function before the fallback:
|
|
223
|
+
|
|
224
|
+
```typescript
|
|
225
|
+
export function chunkFile(content, filePath, opts) {
|
|
226
|
+
// ...existing Markdown and code branches...
|
|
227
|
+
|
|
228
|
+
if (MY_FORMAT_EXTENSIONS.has(ext)) {
|
|
229
|
+
return parseMyFormat(content, opts);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Fallback: paragraph-based splitting
|
|
233
|
+
return splitByParagraphs(content, opts.maxTokens);
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### 3. Add tests
|
|
238
|
+
|
|
239
|
+
Create `src/ingestion/my-format-parser.test.ts`. Test at minimum: empty input, single section, multiple sections, sections exceeding `maxTokens`.
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
|
|
243
|
+
## Running Tests
|
|
244
|
+
|
|
245
|
+
Run all tests once:
|
|
246
|
+
|
|
247
|
+
```bash
|
|
248
|
+
npm test
|
|
249
|
+
# or
|
|
250
|
+
npx vitest run
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
Run in watch mode during development:
|
|
254
|
+
|
|
255
|
+
```bash
|
|
256
|
+
npm run test:watch
|
|
257
|
+
# or
|
|
258
|
+
npx vitest
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
Run a specific test file:
|
|
262
|
+
|
|
263
|
+
```bash
|
|
264
|
+
npx vitest run src/importers/my-tool.test.ts
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
---
|
|
268
|
+
|
|
269
|
+
## Running Benchmarks
|
|
270
|
+
|
|
271
|
+
Once the MCP server is running and connected to your coding assistant, use the eval commands:
|
|
272
|
+
|
|
273
|
+
```
|
|
274
|
+
/memory eval # Live retrieval metrics for current session
|
|
275
|
+
/memory eval --benchmark # Run synthetic benchmark suite
|
|
276
|
+
/memory eval --snapshot baseline # Save current config as a named baseline
|
|
277
|
+
/memory eval --compare baseline # Compare current config against saved baseline
|
|
278
|
+
/memory eval --grow # Add real query misses to benchmark suite
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
A PR that changes retrieval logic, scoring, or compaction thresholds must include a `--benchmark` run showing no regression against the `baseline` snapshot.
|
|
282
|
+
|
|
283
|
+
---
|
|
284
|
+
|
|
285
|
+
## PR Requirements
|
|
286
|
+
|
|
287
|
+
Before opening a pull request:
|
|
288
|
+
|
|
289
|
+
1. **Tests pass** — `npm test` exits 0 with no failures
|
|
290
|
+
2. **Type checker clean** — `npm run typecheck` exits 0
|
|
291
|
+
3. **Build succeeds** — `npm run build` exits 0
|
|
292
|
+
4. **Benchmark does not regress** — run `/memory eval --compare baseline` and include the output in your PR description if you changed retrieval, scoring, or compaction logic
|
|
293
|
+
5. **New behavior is tested** — new importers, parsers, and content types all require corresponding test files
|
|
294
|
+
|
|
295
|
+
If you're adding a new host tool importer, include the `detect()` logic rationale in your PR description — false positives will silently corrupt imports for users who don't have the tool installed.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 strvmarv
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
# total-recall
|
|
2
|
+
|
|
3
|
+
**Multi-tiered memory and knowledge base for TUI coding assistants.**
|
|
4
|
+
|
|
5
|
+
Your AI coding tool forgets everything. total-recall doesn't.
|
|
6
|
+
|
|
7
|
+
A cross-platform plugin that gives Claude Code, GitHub Copilot CLI, OpenCode, Cline, and Cursor persistent, semantically searchable memory with a hierarchical knowledge base — backed by local SQLite + vector embeddings, zero external dependencies.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## The Problem
|
|
12
|
+
|
|
13
|
+
Every TUI coding assistant has the same gap:
|
|
14
|
+
|
|
15
|
+
- **No tiering** — all memories treated equally, leading to context bloat or information loss
|
|
16
|
+
- **Tool-locked** — switching between Claude Code and Copilot means starting from scratch
|
|
17
|
+
- **No knowledge base** — can't ingest your docs and have them retrieved when relevant
|
|
18
|
+
- **No semantic search** — memories retrieved by filename, not by meaning
|
|
19
|
+
- **No observability** — no way to know if memory is helping or just noise
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## The Solution
|
|
24
|
+
|
|
25
|
+
total-recall introduces a three-tier memory model: **Hot** memories (up to 50 entries) are auto-injected into every prompt so your most important context is always present. **Warm** memories (up to 10K entries) are retrieved semantically — when you ask about authentication, relevant auth memories surface automatically. **Cold** storage is unlimited hierarchical knowledge base: ingest your docs, README files, API references, and architecture notes, and they're retrieved when relevant.
|
|
26
|
+
|
|
27
|
+
The knowledge base ingests entire directories — source trees, documentation folders, design specs — and chunks them semantically with heading-aware Markdown parsing and AST-based code parsing. Every chunk is embedded with `all-MiniLM-L6-v2` (384 dimensions, runs locally via ONNX) so retrieval is purely semantic, no keyword matching required.
|
|
28
|
+
|
|
29
|
+
Platform support is via MCP (Model Context Protocol), which means total-recall works with any MCP-compatible tool. Dedicated importers for Claude Code and Copilot CLI mean your existing memories migrate automatically on first run. An eval framework lets you measure retrieval quality, run benchmarks, and compare configuration changes before committing them.
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Installation
|
|
34
|
+
|
|
35
|
+
### Self-Install (Paste Into Any AI Coding Assistant)
|
|
36
|
+
|
|
37
|
+
> Install the total-recall memory plugin: fetch and follow the instructions at https://raw.githubusercontent.com/strvmarv/total-recall/main/INSTALL.md
|
|
38
|
+
|
|
39
|
+
That's it. Your AI assistant will read the instructions and install total-recall for its platform.
|
|
40
|
+
|
|
41
|
+
### Claude Code
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
/plugin install total-recall@strvmarv-marketplace
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Or if the marketplace isn't registered:
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
/plugin marketplace add strvmarv/total-recall-marketplace
|
|
51
|
+
/plugin install total-recall@strvmarv-marketplace
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### npm (Any MCP-Compatible Tool)
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
npm install -g @strvmarv/total-recall
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Then add to your tool's MCP config:
|
|
61
|
+
|
|
62
|
+
```json
|
|
63
|
+
{
|
|
64
|
+
"mcpServers": {
|
|
65
|
+
"total-recall": {
|
|
66
|
+
"command": "npx",
|
|
67
|
+
"args": ["-y", "@strvmarv/total-recall"]
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
This works with **Copilot CLI**, **OpenCode**, **Cline**, **Cursor**, and any other MCP-compatible tool.
|
|
74
|
+
|
|
75
|
+
### From Source
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
git clone https://github.com/strvmarv/total-recall.git
|
|
79
|
+
cd total-recall
|
|
80
|
+
npm install && npm run build
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### First Session
|
|
84
|
+
|
|
85
|
+
total-recall auto-initializes on first use:
|
|
86
|
+
|
|
87
|
+
1. Creates `~/.total-recall/` with SQLite database
|
|
88
|
+
2. Downloads embedding model (~80MB, one-time)
|
|
89
|
+
3. Scans for existing memories (Claude Code, Copilot CLI)
|
|
90
|
+
4. Auto-ingests project docs (README, docs/, etc.)
|
|
91
|
+
5. Reports: `total-recall: initialized · 4 memories imported · 12 docs ingested · system verified`
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## Architecture
|
|
96
|
+
|
|
97
|
+
```
|
|
98
|
+
MCP Server (Node.js/TypeScript)
|
|
99
|
+
├── Always Loaded: SQLite + vec, MCP Tools, Event Logger
|
|
100
|
+
├── Lazy Loaded: ONNX Embedder, Compactor, Ingestor
|
|
101
|
+
└── Host Importers: Claude Code, Copilot CLI, OpenCode
|
|
102
|
+
|
|
103
|
+
Tiers:
|
|
104
|
+
Hot (50 entries) → auto-injected every prompt
|
|
105
|
+
Warm (10K entries) → semantic search per query
|
|
106
|
+
Cold (unlimited) → hierarchical KB retrieval
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
**Data flow:**
|
|
110
|
+
|
|
111
|
+
1. `store` — write a memory, assign tier, embed, persist
|
|
112
|
+
2. `search` — embed query, vector search warm + cold, return ranked results
|
|
113
|
+
3. `compact` — decay scores, demote warm→cold, evict hot→warm, summarize clusters
|
|
114
|
+
4. `ingest` — chunk files, embed chunks, store in cold tier with metadata
|
|
115
|
+
|
|
116
|
+
All state lives in `~/.total-recall/db.sqlite`. The embedding model is cached at `~/.total-recall/models/`. No network calls after initial model download.
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
## Commands
|
|
121
|
+
|
|
122
|
+
| Command | Description |
|
|
123
|
+
|---|---|
|
|
124
|
+
| `/memory status` | Dashboard overview |
|
|
125
|
+
| `/memory search <query>` | Semantic search across all tiers |
|
|
126
|
+
| `/memory ingest <path>` | Add files/dirs to knowledge base |
|
|
127
|
+
| `/memory forget <query>` | Find and delete entries |
|
|
128
|
+
| `/memory compact` | Force compaction with preview |
|
|
129
|
+
| `/memory inspect <id>` | Deep dive on single entry |
|
|
130
|
+
| `/memory promote <id>` | Move entry to higher tier |
|
|
131
|
+
| `/memory demote <id>` | Move entry to lower tier |
|
|
132
|
+
| `/memory export` | Export to portable format |
|
|
133
|
+
| `/memory import <file>` | Import from export file |
|
|
134
|
+
| `/memory eval` | Live performance metrics |
|
|
135
|
+
| `/memory eval --benchmark` | Run synthetic benchmark |
|
|
136
|
+
| `/memory eval --compare <name>` | Compare configs |
|
|
137
|
+
| `/memory eval --snapshot <name>` | Save current config baseline |
|
|
138
|
+
| `/memory eval --grow` | Add real misses to benchmark |
|
|
139
|
+
| `/memory config get <key>` | Read config value |
|
|
140
|
+
| `/memory config set <key> <value>` | Update config |
|
|
141
|
+
| `/memory history` | Show recent tier movements |
|
|
142
|
+
| `/memory lineage <id>` | Show compaction ancestry |
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## Supported Platforms
|
|
147
|
+
|
|
148
|
+
| Platform | Support | Notes |
|
|
149
|
+
|---|---|---|
|
|
150
|
+
| Claude Code | Full | Native plugin, session hooks, auto-import |
|
|
151
|
+
| Copilot CLI | Full | Auto-import from existing Copilot memory files |
|
|
152
|
+
| OpenCode | MCP | Configure MCP server in opencode config |
|
|
153
|
+
| Cline | MCP | Configure MCP server in Cline settings |
|
|
154
|
+
| Cursor | Full | MCP server + `.cursor-plugin/` wrapper |
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## Configuration
|
|
159
|
+
|
|
160
|
+
Copy `~/.total-recall/config.toml` to override defaults:
|
|
161
|
+
|
|
162
|
+
```toml
|
|
163
|
+
# total-recall configuration
|
|
164
|
+
|
|
165
|
+
[tiers.hot]
|
|
166
|
+
max_entries = 50 # Max entries auto-injected per prompt
|
|
167
|
+
token_budget = 4000 # Max tokens for hot tier injection
|
|
168
|
+
carry_forward_threshold = 0.7 # Score threshold to stay in hot
|
|
169
|
+
|
|
170
|
+
[tiers.warm]
|
|
171
|
+
max_entries = 10000 # Max entries in warm tier
|
|
172
|
+
retrieval_top_k = 5 # Results returned per search
|
|
173
|
+
similarity_threshold = 0.65 # Min cosine similarity for retrieval
|
|
174
|
+
cold_decay_days = 30 # Days before unused warm entries decay to cold
|
|
175
|
+
|
|
176
|
+
[tiers.cold]
|
|
177
|
+
chunk_max_tokens = 512 # Max tokens per knowledge base chunk
|
|
178
|
+
chunk_overlap_tokens = 50 # Overlap between adjacent chunks
|
|
179
|
+
lazy_summary_threshold = 5 # Accesses before generating summary
|
|
180
|
+
|
|
181
|
+
[compaction]
|
|
182
|
+
decay_half_life_hours = 168 # Score half-life (168h = 1 week)
|
|
183
|
+
warm_threshold = 0.3 # Score below which warm→cold
|
|
184
|
+
promote_threshold = 0.7 # Score above which cold→warm
|
|
185
|
+
warm_sweep_interval_days = 7 # How often to run warm sweep
|
|
186
|
+
|
|
187
|
+
[embedding]
|
|
188
|
+
model = "all-MiniLM-L6-v2" # Embedding model name
|
|
189
|
+
dimensions = 384 # Embedding dimensions
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
## Extending
|
|
195
|
+
|
|
196
|
+
### Adding a New Host Tool
|
|
197
|
+
|
|
198
|
+
Implement the `HostImporter` interface (~50 lines). It requires four methods: `detect()` to check if the tool is present, `scan()` to report what's available, `importMemories()` to migrate existing memories, and `importKnowledge()` to migrate knowledge files. See [CONTRIBUTING.md](CONTRIBUTING.md) for a full example.
|
|
199
|
+
|
|
200
|
+
### Adding a New Content Type
|
|
201
|
+
|
|
202
|
+
Add a row to the `content_type` lookup table in `src/db/schema.ts`, then register a type weight for relevance scoring. No other changes needed.
|
|
203
|
+
|
|
204
|
+
### Adding a New Chunking Parser
|
|
205
|
+
|
|
206
|
+
Implement the `Chunk[]`-returning parser interface and register it in `src/ingestion/chunker.ts` alongside the existing Markdown and code parsers. See [CONTRIBUTING.md](CONTRIBUTING.md) for the interface definition.
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
## Built With & Inspired By
|
|
211
|
+
|
|
212
|
+
### [superpowers](https://github.com/obra/superpowers) by [obra](https://github.com/obra)
|
|
213
|
+
|
|
214
|
+
total-recall's plugin architecture, skill format, hook system, multi-platform wrapper pattern, and development philosophy are directly inspired by and modeled after the **superpowers** plugin. superpowers demonstrated that a zero-dependency, markdown-driven skill system could fundamentally improve how AI coding assistants behave — total-recall extends that same philosophy to memory and knowledge management.
|
|
215
|
+
|
|
216
|
+
Specific patterns we learned from superpowers:
|
|
217
|
+
|
|
218
|
+
- **SKILL.md format** with YAML frontmatter and trigger-condition-focused descriptions
|
|
219
|
+
- **SessionStart hooks** for injecting core behavior at session start
|
|
220
|
+
- **Multi-platform wrappers** (`.claude-plugin/`, `.copilot-plugin/`, `.cursor-plugin/`, `.opencode/`)
|
|
221
|
+
- **Subagent architecture** for isolated, focused task execution
|
|
222
|
+
- **Zero-dependency philosophy** — no external services, no API keys, no cloud
|
|
223
|
+
- **Two-stage review pattern** for quality assurance
|
|
224
|
+
|
|
225
|
+
If you're building plugins for TUI coding assistants, start with [superpowers](https://github.com/obra/superpowers). It's the foundation this ecosystem needs.
|
|
226
|
+
|
|
227
|
+
### Core Technologies
|
|
228
|
+
|
|
229
|
+
- [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) — Fast, synchronous SQLite bindings
|
|
230
|
+
- [sqlite-vec](https://github.com/asg017/sqlite-vec) — Vector similarity search in SQLite
|
|
231
|
+
- [onnxruntime-node](https://github.com/microsoft/onnxruntime) — Local ML inference
|
|
232
|
+
- [all-MiniLM-L6-v2](https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2) — Sentence embeddings (384d)
|
|
233
|
+
- [@modelcontextprotocol/sdk](https://github.com/modelcontextprotocol/sdk) — MCP server implementation
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
## License
|
|
238
|
+
|
|
239
|
+
MIT — see [LICENSE](LICENSE)
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: compactor
|
|
3
|
+
description: |
|
|
4
|
+
Use this agent at session end to perform intelligent hot-to-warm compaction.
|
|
5
|
+
Reviews hot tier entries, groups related items, generates summaries, and
|
|
6
|
+
measures semantic drift to ensure compaction quality.
|
|
7
|
+
model: inherit
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Compactor Agent
|
|
11
|
+
|
|
12
|
+
You are the total-recall compactor. Your job is to review hot tier memory entries at session end and decide what to keep, promote, merge, or discard.
|
|
13
|
+
|
|
14
|
+
## Input
|
|
15
|
+
|
|
16
|
+
You receive the current hot tier entries with their decay scores, access counts, and content.
|
|
17
|
+
|
|
18
|
+
## Process
|
|
19
|
+
|
|
20
|
+
1. Group related entries (e.g., multiple corrections about the same topic)
|
|
21
|
+
2. For groups of 2+ related entries:
|
|
22
|
+
- Generate a concise summary that preserves all key facts
|
|
23
|
+
- The summary should be retrievable by the same queries that would find the originals
|
|
24
|
+
3. For individual entries:
|
|
25
|
+
- If decay score > promote_threshold: recommend carry forward
|
|
26
|
+
- If decay score > warm_threshold: recommend promote to warm as-is
|
|
27
|
+
- If decay score < warm_threshold: recommend discard
|
|
28
|
+
4. Report: entries processed, summaries generated, facts preserved count
|
|
29
|
+
|
|
30
|
+
## Output Format
|
|
31
|
+
|
|
32
|
+
Return a JSON array of decisions:
|
|
33
|
+
|
|
34
|
+
```json
|
|
35
|
+
[
|
|
36
|
+
{"action": "carry_forward", "entry_ids": ["id1"]},
|
|
37
|
+
{"action": "promote", "entry_ids": ["id2", "id3"], "summary": "merged summary text"},
|
|
38
|
+
{"action": "discard", "entry_ids": ["id4"], "reason": "ephemeral session context"}
|
|
39
|
+
]
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Rules
|
|
43
|
+
|
|
44
|
+
- NEVER discard corrections or preferences with decay score > 0.2
|
|
45
|
+
- ALWAYS preserve the specific details (tool names, version numbers, config values)
|
|
46
|
+
- Summaries must be shorter than the combined originals
|
|
47
|
+
- If unsure, promote rather than discard
|