gnosys-mcp 0.1.0 → 0.2.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 +52 -15
- package/package.json +4 -2
- package/dist/test/bootstrap.test.d.ts +0 -2
- package/dist/test/bootstrap.test.d.ts.map +0 -1
- package/dist/test/bootstrap.test.js +0 -208
- package/dist/test/bootstrap.test.js.map +0 -1
- package/dist/test/history.test.d.ts +0 -2
- package/dist/test/history.test.d.ts.map +0 -1
- package/dist/test/history.test.js +0 -141
- package/dist/test/history.test.js.map +0 -1
- package/dist/test/ingest.test.d.ts +0 -2
- package/dist/test/ingest.test.d.ts.map +0 -1
- package/dist/test/ingest.test.js +0 -132
- package/dist/test/ingest.test.js.map +0 -1
- package/dist/test/lensing.test.d.ts +0 -2
- package/dist/test/lensing.test.d.ts.map +0 -1
- package/dist/test/lensing.test.js +0 -154
- package/dist/test/lensing.test.js.map +0 -1
- package/dist/test/search.test.d.ts +0 -2
- package/dist/test/search.test.d.ts.map +0 -1
- package/dist/test/search.test.js +0 -129
- package/dist/test/search.test.js.map +0 -1
- package/dist/test/store.test.d.ts +0 -2
- package/dist/test/store.test.d.ts.map +0 -1
- package/dist/test/store.test.js +0 -152
- package/dist/test/store.test.js.map +0 -1
- package/dist/test/tags.test.d.ts +0 -2
- package/dist/test/tags.test.d.ts.map +0 -1
- package/dist/test/tags.test.js +0 -81
- package/dist/test/tags.test.js.map +0 -1
- package/dist/test/timeline.test.d.ts +0 -2
- package/dist/test/timeline.test.d.ts.map +0 -1
- package/dist/test/timeline.test.js +0 -127
- package/dist/test/timeline.test.js.map +0 -1
- package/dist/test/wikilinks.test.d.ts +0 -2
- package/dist/test/wikilinks.test.d.ts.map +0 -1
- package/dist/test/wikilinks.test.js +0 -178
- package/dist/test/wikilinks.test.js.map +0 -1
package/README.md
CHANGED
|
@@ -4,6 +4,13 @@
|
|
|
4
4
|
|
|
5
5
|
<p align="center"><strong>LLM-native persistent memory for AI agents.</strong></p>
|
|
6
6
|
|
|
7
|
+
<p align="center">
|
|
8
|
+
<a href="https://www.npmjs.com/package/gnosys-mcp"><img src="https://img.shields.io/npm/v/gnosys-mcp.svg" alt="npm version"></a>
|
|
9
|
+
<a href="https://github.com/proticom/gnosys-mcp/actions"><img src="https://github.com/proticom/gnosys-mcp/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
|
|
10
|
+
<a href="https://gnosys.ai"><img src="https://img.shields.io/badge/docs-gnosys.ai-C04C4C" alt="docs"></a>
|
|
11
|
+
<a href="https://github.com/proticom/gnosys-mcp/blob/master/LICENSE"><img src="https://img.shields.io/npm/l/gnosys-mcp.svg" alt="license"></a>
|
|
12
|
+
</p>
|
|
13
|
+
|
|
7
14
|
---
|
|
8
15
|
|
|
9
16
|
Gnosys gives AI agents long-term memory that survives across sessions. Memories are atomic markdown files with structured frontmatter, stored in plain directories, versioned by git, and searchable via FTS5. No database, no vector store, no external services — just files.
|
|
@@ -92,15 +99,8 @@ Edits made in Obsidian are picked up automatically by Gnosys (the filesystem is
|
|
|
92
99
|
|
|
93
100
|
## CLI Reference
|
|
94
101
|
|
|
95
|
-
Install globally or run via `npx`:
|
|
96
|
-
|
|
97
102
|
```bash
|
|
98
|
-
# Global install
|
|
99
103
|
npm install -g gnosys-mcp
|
|
100
|
-
|
|
101
|
-
# Or run from the project
|
|
102
|
-
npm run build
|
|
103
|
-
node dist/cli.js <command>
|
|
104
104
|
```
|
|
105
105
|
|
|
106
106
|
### Core Commands
|
|
@@ -160,6 +160,24 @@ gnosys <command> --help # Same as above
|
|
|
160
160
|
|
|
161
161
|
## MCP Server Setup
|
|
162
162
|
|
|
163
|
+
### Claude Desktop
|
|
164
|
+
|
|
165
|
+
Add to `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or the equivalent config on your platform:
|
|
166
|
+
|
|
167
|
+
```json
|
|
168
|
+
{
|
|
169
|
+
"mcpServers": {
|
|
170
|
+
"gnosys": {
|
|
171
|
+
"command": "npx",
|
|
172
|
+
"args": ["gnosys-mcp"],
|
|
173
|
+
"env": {
|
|
174
|
+
"ANTHROPIC_API_KEY": "your-key-here"
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
163
181
|
### Cursor
|
|
164
182
|
|
|
165
183
|
Add to your Cursor MCP settings (`.cursor/mcp.json` in your project or global config):
|
|
@@ -168,8 +186,8 @@ Add to your Cursor MCP settings (`.cursor/mcp.json` in your project or global co
|
|
|
168
186
|
{
|
|
169
187
|
"mcpServers": {
|
|
170
188
|
"gnosys": {
|
|
171
|
-
"command": "
|
|
172
|
-
"args": ["
|
|
189
|
+
"command": "npx",
|
|
190
|
+
"args": ["gnosys-mcp"],
|
|
173
191
|
"env": {
|
|
174
192
|
"ANTHROPIC_API_KEY": "your-key-here"
|
|
175
193
|
}
|
|
@@ -178,16 +196,35 @@ Add to your Cursor MCP settings (`.cursor/mcp.json` in your project or global co
|
|
|
178
196
|
}
|
|
179
197
|
```
|
|
180
198
|
|
|
181
|
-
### Claude
|
|
199
|
+
### Claude Code
|
|
182
200
|
|
|
183
|
-
|
|
201
|
+
```bash
|
|
202
|
+
claude mcp add gnosys npx gnosys-mcp
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### Codex
|
|
206
|
+
|
|
207
|
+
Add to `~/.codex/config.toml` or `.codex/config.toml` in your project:
|
|
208
|
+
|
|
209
|
+
```toml
|
|
210
|
+
[mcp.gnosys]
|
|
211
|
+
type = "local"
|
|
212
|
+
command = ["npx", "gnosys-mcp"]
|
|
213
|
+
|
|
214
|
+
[mcp.gnosys.env]
|
|
215
|
+
ANTHROPIC_API_KEY = "your-key-here"
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### OpenCode
|
|
219
|
+
|
|
220
|
+
Add to `~/.config/opencode/opencode.json` or `opencode.json` in your project:
|
|
184
221
|
|
|
185
222
|
```json
|
|
186
223
|
{
|
|
187
|
-
"
|
|
224
|
+
"mcp": {
|
|
188
225
|
"gnosys": {
|
|
189
|
-
"
|
|
190
|
-
"
|
|
226
|
+
"type": "local",
|
|
227
|
+
"command": ["npx", "gnosys-mcp"],
|
|
191
228
|
"env": {
|
|
192
229
|
"ANTHROPIC_API_KEY": "your-key-here"
|
|
193
230
|
}
|
|
@@ -273,7 +310,7 @@ ANTHROPIC_API_KEY=sk-ant-...
|
|
|
273
310
|
```bash
|
|
274
311
|
npm install # Install dependencies
|
|
275
312
|
npm run build # Compile TypeScript
|
|
276
|
-
npm test # Run test suite
|
|
313
|
+
npm test # Run test suite
|
|
277
314
|
npm run test:watch # Run tests in watch mode
|
|
278
315
|
npm run dev # Run MCP server in dev mode (tsx)
|
|
279
316
|
```
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gnosys-mcp",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Gnosys
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Gnosys — LLM-native persistent memory system. MCP server + CLI.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"bin": {
|
|
@@ -37,6 +37,7 @@
|
|
|
37
37
|
},
|
|
38
38
|
"files": [
|
|
39
39
|
"dist",
|
|
40
|
+
"!dist/test",
|
|
40
41
|
"README.md"
|
|
41
42
|
],
|
|
42
43
|
"keywords": [
|
|
@@ -49,6 +50,7 @@
|
|
|
49
50
|
"agent"
|
|
50
51
|
],
|
|
51
52
|
"license": "MIT",
|
|
53
|
+
"homepage": "https://gnosys.ai",
|
|
52
54
|
"repository": {
|
|
53
55
|
"type": "git",
|
|
54
56
|
"url": "https://github.com/proticom/gnosys-mcp"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"bootstrap.test.d.ts","sourceRoot":"","sources":["../../src/test/bootstrap.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,208 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
2
|
-
import fs from "fs/promises";
|
|
3
|
-
import path from "path";
|
|
4
|
-
import os from "os";
|
|
5
|
-
import { discoverFiles, parseFileForImport, bootstrap } from "../lib/bootstrap.js";
|
|
6
|
-
import { GnosysStore } from "../lib/store.js";
|
|
7
|
-
let tempDir;
|
|
8
|
-
let sourceDir;
|
|
9
|
-
let storeDir;
|
|
10
|
-
beforeEach(async () => {
|
|
11
|
-
tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "gnosys-bootstrap-test-"));
|
|
12
|
-
sourceDir = path.join(tempDir, "source");
|
|
13
|
-
storeDir = path.join(tempDir, "store");
|
|
14
|
-
await fs.mkdir(sourceDir, { recursive: true });
|
|
15
|
-
await fs.mkdir(storeDir, { recursive: true });
|
|
16
|
-
});
|
|
17
|
-
afterEach(async () => {
|
|
18
|
-
await fs.rm(tempDir, { recursive: true, force: true });
|
|
19
|
-
});
|
|
20
|
-
describe("discoverFiles", () => {
|
|
21
|
-
it("finds markdown files", async () => {
|
|
22
|
-
await fs.writeFile(path.join(sourceDir, "doc1.md"), "# Doc 1");
|
|
23
|
-
await fs.writeFile(path.join(sourceDir, "doc2.md"), "# Doc 2");
|
|
24
|
-
await fs.writeFile(path.join(sourceDir, "readme.txt"), "Not markdown");
|
|
25
|
-
const files = await discoverFiles(sourceDir);
|
|
26
|
-
expect(files).toHaveLength(2);
|
|
27
|
-
expect(files).toContain("doc1.md");
|
|
28
|
-
expect(files).toContain("doc2.md");
|
|
29
|
-
});
|
|
30
|
-
it("finds files in subdirectories", async () => {
|
|
31
|
-
await fs.mkdir(path.join(sourceDir, "decisions"), { recursive: true });
|
|
32
|
-
await fs.writeFile(path.join(sourceDir, "decisions", "auth.md"), "# Auth");
|
|
33
|
-
const files = await discoverFiles(sourceDir);
|
|
34
|
-
expect(files).toHaveLength(1);
|
|
35
|
-
expect(files[0]).toBe("decisions/auth.md");
|
|
36
|
-
});
|
|
37
|
-
it("supports custom patterns", async () => {
|
|
38
|
-
await fs.writeFile(path.join(sourceDir, "doc.md"), "# Doc");
|
|
39
|
-
await fs.writeFile(path.join(sourceDir, "notes.txt"), "Notes");
|
|
40
|
-
const files = await discoverFiles(sourceDir, ["**/*.md", "**/*.txt"]);
|
|
41
|
-
expect(files).toHaveLength(2);
|
|
42
|
-
});
|
|
43
|
-
it("returns empty for empty directory", async () => {
|
|
44
|
-
const files = await discoverFiles(sourceDir);
|
|
45
|
-
expect(files).toHaveLength(0);
|
|
46
|
-
});
|
|
47
|
-
it("deduplicates files across patterns", async () => {
|
|
48
|
-
await fs.writeFile(path.join(sourceDir, "doc.md"), "# Doc");
|
|
49
|
-
const files = await discoverFiles(sourceDir, ["**/*.md", "*.md"]);
|
|
50
|
-
expect(files).toHaveLength(1);
|
|
51
|
-
});
|
|
52
|
-
});
|
|
53
|
-
describe("parseFileForImport", () => {
|
|
54
|
-
const defaultOpts = {
|
|
55
|
-
sourceDir: "/tmp",
|
|
56
|
-
defaultCategory: "imported",
|
|
57
|
-
preserveFrontmatter: false,
|
|
58
|
-
};
|
|
59
|
-
it("extracts title from H1 heading", () => {
|
|
60
|
-
const { frontmatter } = parseFileForImport("# My Decision\n\nContent here.", "doc.md", defaultOpts);
|
|
61
|
-
expect(frontmatter.title).toBe("My Decision");
|
|
62
|
-
});
|
|
63
|
-
it("falls back to filename for title", () => {
|
|
64
|
-
const { frontmatter } = parseFileForImport("No heading here.", "my-cool-doc.md", defaultOpts);
|
|
65
|
-
expect(frontmatter.title).toBe("My Cool Doc");
|
|
66
|
-
});
|
|
67
|
-
it("infers category from directory structure", () => {
|
|
68
|
-
const { frontmatter } = parseFileForImport("# Test", "decisions/auth.md", defaultOpts);
|
|
69
|
-
expect(frontmatter.category).toBe("decisions");
|
|
70
|
-
});
|
|
71
|
-
it("uses default category for root files", () => {
|
|
72
|
-
const { frontmatter } = parseFileForImport("# Test", "doc.md", defaultOpts);
|
|
73
|
-
expect(frontmatter.category).toBe("imported");
|
|
74
|
-
});
|
|
75
|
-
it("preserves existing frontmatter when option is set", () => {
|
|
76
|
-
const content = `---
|
|
77
|
-
title: Custom Title
|
|
78
|
-
category: architecture
|
|
79
|
-
confidence: 0.95
|
|
80
|
-
author: human
|
|
81
|
-
---
|
|
82
|
-
# Custom Title
|
|
83
|
-
|
|
84
|
-
Content here.`;
|
|
85
|
-
const { frontmatter } = parseFileForImport(content, "doc.md", {
|
|
86
|
-
...defaultOpts,
|
|
87
|
-
preserveFrontmatter: true,
|
|
88
|
-
});
|
|
89
|
-
expect(frontmatter.title).toBe("Custom Title");
|
|
90
|
-
expect(frontmatter.category).toBe("architecture");
|
|
91
|
-
expect(frontmatter.confidence).toBe(0.95);
|
|
92
|
-
expect(frontmatter.author).toBe("human");
|
|
93
|
-
});
|
|
94
|
-
it("uses defaults when not preserving frontmatter", () => {
|
|
95
|
-
const content = `---
|
|
96
|
-
title: Custom Title
|
|
97
|
-
category: architecture
|
|
98
|
-
---
|
|
99
|
-
# Custom Title`;
|
|
100
|
-
const { frontmatter } = parseFileForImport(content, "doc.md", defaultOpts);
|
|
101
|
-
// Should NOT preserve even though frontmatter exists
|
|
102
|
-
expect(frontmatter.category).toBe("imported");
|
|
103
|
-
expect(frontmatter.authority).toBe("imported");
|
|
104
|
-
});
|
|
105
|
-
it("strips frontmatter from body", () => {
|
|
106
|
-
const content = `---
|
|
107
|
-
title: Test
|
|
108
|
-
---
|
|
109
|
-
# Test
|
|
110
|
-
|
|
111
|
-
Body content.`;
|
|
112
|
-
const { body } = parseFileForImport(content, "doc.md", defaultOpts);
|
|
113
|
-
expect(body).toBe("# Test\n\nBody content.");
|
|
114
|
-
expect(body).not.toContain("---");
|
|
115
|
-
});
|
|
116
|
-
});
|
|
117
|
-
describe("bootstrap", () => {
|
|
118
|
-
it("imports files into the store", async () => {
|
|
119
|
-
await fs.writeFile(path.join(sourceDir, "doc1.md"), "# First Doc\n\nContent one.");
|
|
120
|
-
await fs.writeFile(path.join(sourceDir, "doc2.md"), "# Second Doc\n\nContent two.");
|
|
121
|
-
const store = new GnosysStore(storeDir);
|
|
122
|
-
await store.init();
|
|
123
|
-
const result = await bootstrap(store, {
|
|
124
|
-
sourceDir,
|
|
125
|
-
defaultCategory: "imported",
|
|
126
|
-
});
|
|
127
|
-
expect(result.totalScanned).toBe(2);
|
|
128
|
-
expect(result.imported).toHaveLength(2);
|
|
129
|
-
expect(result.skipped).toHaveLength(0);
|
|
130
|
-
expect(result.failed).toHaveLength(0);
|
|
131
|
-
// Verify files were written
|
|
132
|
-
const memories = await store.getAllMemories();
|
|
133
|
-
expect(memories).toHaveLength(2);
|
|
134
|
-
});
|
|
135
|
-
it("skips existing memories when skipExisting is true", async () => {
|
|
136
|
-
const store = new GnosysStore(storeDir);
|
|
137
|
-
await store.init();
|
|
138
|
-
// Write a memory first
|
|
139
|
-
await store.writeMemory("imported", "first-doc.md", {
|
|
140
|
-
id: "existing-1",
|
|
141
|
-
title: "First Doc",
|
|
142
|
-
category: "imported",
|
|
143
|
-
tags: { domain: [] },
|
|
144
|
-
relevance: "",
|
|
145
|
-
author: "human",
|
|
146
|
-
authority: "imported",
|
|
147
|
-
confidence: 0.7,
|
|
148
|
-
created: "2026-03-01",
|
|
149
|
-
modified: "2026-03-01",
|
|
150
|
-
status: "active",
|
|
151
|
-
supersedes: null,
|
|
152
|
-
}, "# First Doc\n\nExisting content.");
|
|
153
|
-
// Now try to bootstrap with the same title
|
|
154
|
-
await fs.writeFile(path.join(sourceDir, "doc1.md"), "# First Doc\n\nNew content.");
|
|
155
|
-
await fs.writeFile(path.join(sourceDir, "doc2.md"), "# Second Doc\n\nContent two.");
|
|
156
|
-
const result = await bootstrap(store, {
|
|
157
|
-
sourceDir,
|
|
158
|
-
skipExisting: true,
|
|
159
|
-
defaultCategory: "imported",
|
|
160
|
-
});
|
|
161
|
-
expect(result.imported).toHaveLength(1);
|
|
162
|
-
expect(result.skipped).toHaveLength(1);
|
|
163
|
-
expect(result.skipped[0]).toBe("doc1.md");
|
|
164
|
-
});
|
|
165
|
-
it("supports dry run mode", async () => {
|
|
166
|
-
await fs.writeFile(path.join(sourceDir, "doc1.md"), "# Test\n\nContent.");
|
|
167
|
-
const store = new GnosysStore(storeDir);
|
|
168
|
-
await store.init();
|
|
169
|
-
const result = await bootstrap(store, {
|
|
170
|
-
sourceDir,
|
|
171
|
-
dryRun: true,
|
|
172
|
-
});
|
|
173
|
-
expect(result.imported).toHaveLength(1);
|
|
174
|
-
// Verify nothing was actually written
|
|
175
|
-
const memories = await store.getAllMemories();
|
|
176
|
-
expect(memories).toHaveLength(0);
|
|
177
|
-
});
|
|
178
|
-
it("handles empty source directory", async () => {
|
|
179
|
-
const store = new GnosysStore(storeDir);
|
|
180
|
-
await store.init();
|
|
181
|
-
const result = await bootstrap(store, { sourceDir });
|
|
182
|
-
expect(result.totalScanned).toBe(0);
|
|
183
|
-
expect(result.imported).toHaveLength(0);
|
|
184
|
-
});
|
|
185
|
-
it("respects custom patterns", async () => {
|
|
186
|
-
await fs.writeFile(path.join(sourceDir, "doc.md"), "# MD");
|
|
187
|
-
await fs.writeFile(path.join(sourceDir, "notes.txt"), "TXT content");
|
|
188
|
-
const store = new GnosysStore(storeDir);
|
|
189
|
-
await store.init();
|
|
190
|
-
const result = await bootstrap(store, {
|
|
191
|
-
sourceDir,
|
|
192
|
-
patterns: ["**/*.txt"],
|
|
193
|
-
});
|
|
194
|
-
expect(result.totalScanned).toBe(1);
|
|
195
|
-
expect(result.imported).toHaveLength(1);
|
|
196
|
-
});
|
|
197
|
-
it("preserves category from subdirectory structure", async () => {
|
|
198
|
-
await fs.mkdir(path.join(sourceDir, "architecture"), { recursive: true });
|
|
199
|
-
await fs.writeFile(path.join(sourceDir, "architecture", "layers.md"), "# Three Layers\n\nContent.");
|
|
200
|
-
const store = new GnosysStore(storeDir);
|
|
201
|
-
await store.init();
|
|
202
|
-
await bootstrap(store, { sourceDir });
|
|
203
|
-
const memories = await store.getAllMemories();
|
|
204
|
-
expect(memories).toHaveLength(1);
|
|
205
|
-
expect(memories[0].frontmatter.category).toBe("architecture");
|
|
206
|
-
});
|
|
207
|
-
});
|
|
208
|
-
//# sourceMappingURL=bootstrap.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"bootstrap.test.js","sourceRoot":"","sources":["../../src/test/bootstrap.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"history.test.d.ts","sourceRoot":"","sources":["../../src/test/history.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
2
|
-
import fs from "fs/promises";
|
|
3
|
-
import os from "os";
|
|
4
|
-
import path from "path";
|
|
5
|
-
import { execSync } from "child_process";
|
|
6
|
-
import { GnosysStore } from "../lib/store.js";
|
|
7
|
-
import { getFileHistory, getFileAtCommit, rollbackToCommit, hasGitHistory, getFileDiff, } from "../lib/history.js";
|
|
8
|
-
let tmpDir;
|
|
9
|
-
let store;
|
|
10
|
-
function makeFrontmatter(overrides = {}) {
|
|
11
|
-
return {
|
|
12
|
-
id: "test-001",
|
|
13
|
-
title: "Test Memory",
|
|
14
|
-
category: "decisions",
|
|
15
|
-
tags: { domain: ["testing"], type: ["decision"] },
|
|
16
|
-
relevance: "test history rollback versioning",
|
|
17
|
-
author: "human",
|
|
18
|
-
authority: "declared",
|
|
19
|
-
confidence: 0.8,
|
|
20
|
-
created: "2026-03-01",
|
|
21
|
-
modified: "2026-03-01",
|
|
22
|
-
status: "active",
|
|
23
|
-
supersedes: null,
|
|
24
|
-
...overrides,
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
|
-
beforeEach(async () => {
|
|
28
|
-
tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "gnosys-history-"));
|
|
29
|
-
store = new GnosysStore(tmpDir);
|
|
30
|
-
await store.init();
|
|
31
|
-
// Configure git user for commits in this temp directory
|
|
32
|
-
execSync('git config user.email "test@gnosys.dev"', { cwd: tmpDir, stdio: "pipe" });
|
|
33
|
-
execSync('git config user.name "Gnosys Test"', { cwd: tmpDir, stdio: "pipe" });
|
|
34
|
-
});
|
|
35
|
-
afterEach(async () => {
|
|
36
|
-
await fs.rm(tmpDir, { recursive: true, force: true });
|
|
37
|
-
});
|
|
38
|
-
describe("hasGitHistory", () => {
|
|
39
|
-
it("returns true for a git-initialized store", () => {
|
|
40
|
-
expect(hasGitHistory(tmpDir)).toBe(true);
|
|
41
|
-
});
|
|
42
|
-
it("returns false for a non-git directory", async () => {
|
|
43
|
-
const plainDir = await fs.mkdtemp(path.join(os.tmpdir(), "no-git-"));
|
|
44
|
-
expect(hasGitHistory(plainDir)).toBe(false);
|
|
45
|
-
await fs.rm(plainDir, { recursive: true, force: true });
|
|
46
|
-
});
|
|
47
|
-
});
|
|
48
|
-
describe("getFileHistory", () => {
|
|
49
|
-
it("returns history after write and update", async () => {
|
|
50
|
-
const fm = makeFrontmatter();
|
|
51
|
-
await store.writeMemory("decisions", "auth.md", fm, "# Auth\n\nVersion 1");
|
|
52
|
-
// Update the memory
|
|
53
|
-
await store.updateMemory("decisions/auth.md", { title: "Auth Updated", confidence: 0.9 });
|
|
54
|
-
const history = getFileHistory(tmpDir, "decisions/auth.md");
|
|
55
|
-
expect(history.length).toBeGreaterThanOrEqual(2);
|
|
56
|
-
expect(history[0].message).toContain("Update memory");
|
|
57
|
-
expect(history[1].message).toContain("Add memory");
|
|
58
|
-
});
|
|
59
|
-
it("returns empty array for non-existent file", () => {
|
|
60
|
-
const history = getFileHistory(tmpDir, "no/such/file.md");
|
|
61
|
-
expect(history).toEqual([]);
|
|
62
|
-
});
|
|
63
|
-
it("respects the limit parameter", async () => {
|
|
64
|
-
const fm = makeFrontmatter();
|
|
65
|
-
await store.writeMemory("decisions", "multi.md", fm, "# V1");
|
|
66
|
-
await store.updateMemory("decisions/multi.md", { title: "V2" });
|
|
67
|
-
await store.updateMemory("decisions/multi.md", { title: "V3" });
|
|
68
|
-
await store.updateMemory("decisions/multi.md", { title: "V4" });
|
|
69
|
-
const limited = getFileHistory(tmpDir, "decisions/multi.md", 2);
|
|
70
|
-
expect(limited).toHaveLength(2);
|
|
71
|
-
});
|
|
72
|
-
});
|
|
73
|
-
describe("getFileAtCommit", () => {
|
|
74
|
-
it("retrieves file content at a specific commit", async () => {
|
|
75
|
-
const fm = makeFrontmatter({ title: "Original Title" });
|
|
76
|
-
await store.writeMemory("decisions", "version.md", fm, "# Original\n\nOriginal content");
|
|
77
|
-
// Get the first commit hash
|
|
78
|
-
const history1 = getFileHistory(tmpDir, "decisions/version.md");
|
|
79
|
-
const firstHash = history1[0].commitHash;
|
|
80
|
-
// Update
|
|
81
|
-
await store.updateMemory("decisions/version.md", { title: "Changed Title" });
|
|
82
|
-
// Retrieve original version
|
|
83
|
-
const original = getFileAtCommit(tmpDir, "decisions/version.md", firstHash);
|
|
84
|
-
expect(original).toBeTruthy();
|
|
85
|
-
expect(original).toContain("Original Title");
|
|
86
|
-
});
|
|
87
|
-
it("returns null for invalid commit hash", () => {
|
|
88
|
-
const result = getFileAtCommit(tmpDir, "decisions/version.md", "0000000000");
|
|
89
|
-
expect(result).toBeNull();
|
|
90
|
-
});
|
|
91
|
-
});
|
|
92
|
-
describe("getFileDiff", () => {
|
|
93
|
-
it("shows diff between two commits", async () => {
|
|
94
|
-
const fm = makeFrontmatter({ title: "First" });
|
|
95
|
-
await store.writeMemory("decisions", "diff.md", fm, "# First\n\nContent A");
|
|
96
|
-
const history1 = getFileHistory(tmpDir, "decisions/diff.md");
|
|
97
|
-
const hash1 = history1[0].commitHash;
|
|
98
|
-
await store.updateMemory("decisions/diff.md", { title: "Second" }, "# Second\n\nContent B");
|
|
99
|
-
const history2 = getFileHistory(tmpDir, "decisions/diff.md");
|
|
100
|
-
const hash2 = history2[0].commitHash;
|
|
101
|
-
const diff = getFileDiff(tmpDir, "decisions/diff.md", hash1, hash2);
|
|
102
|
-
expect(diff).toBeTruthy();
|
|
103
|
-
expect(diff).toContain("First");
|
|
104
|
-
expect(diff).toContain("Second");
|
|
105
|
-
});
|
|
106
|
-
});
|
|
107
|
-
describe("rollbackToCommit", () => {
|
|
108
|
-
it("reverts a memory to a prior version", async () => {
|
|
109
|
-
const fm = makeFrontmatter({ title: "Original" });
|
|
110
|
-
await store.writeMemory("decisions", "rollback.md", fm, "# Original\n\nOriginal content");
|
|
111
|
-
const historyBefore = getFileHistory(tmpDir, "decisions/rollback.md");
|
|
112
|
-
const originalHash = historyBefore[0].commitHash;
|
|
113
|
-
// Update to new version
|
|
114
|
-
await store.updateMemory("decisions/rollback.md", { title: "Changed" }, "# Changed\n\nNew content");
|
|
115
|
-
// Verify it changed
|
|
116
|
-
const current = await store.readMemory("decisions/rollback.md");
|
|
117
|
-
expect(current?.frontmatter.title).toBe("Changed");
|
|
118
|
-
// Rollback
|
|
119
|
-
const success = rollbackToCommit(tmpDir, "decisions/rollback.md", originalHash);
|
|
120
|
-
expect(success).toBe(true);
|
|
121
|
-
// Verify it reverted
|
|
122
|
-
const reverted = await store.readMemory("decisions/rollback.md");
|
|
123
|
-
expect(reverted?.frontmatter.title).toBe("Original");
|
|
124
|
-
expect(reverted?.content).toContain("Original content");
|
|
125
|
-
});
|
|
126
|
-
it("creates a new commit for the rollback", async () => {
|
|
127
|
-
const fm = makeFrontmatter({ title: "Start" });
|
|
128
|
-
await store.writeMemory("decisions", "rb2.md", fm, "# Start");
|
|
129
|
-
const h1 = getFileHistory(tmpDir, "decisions/rb2.md");
|
|
130
|
-
await store.updateMemory("decisions/rb2.md", { title: "Middle" });
|
|
131
|
-
rollbackToCommit(tmpDir, "decisions/rb2.md", h1[0].commitHash);
|
|
132
|
-
const finalHistory = getFileHistory(tmpDir, "decisions/rb2.md");
|
|
133
|
-
expect(finalHistory.length).toBeGreaterThanOrEqual(3); // write, update, rollback
|
|
134
|
-
expect(finalHistory[0].message).toContain("Rollback");
|
|
135
|
-
});
|
|
136
|
-
it("returns false for invalid commit hash", () => {
|
|
137
|
-
const result = rollbackToCommit(tmpDir, "decisions/nope.md", "0000000");
|
|
138
|
-
expect(result).toBe(false);
|
|
139
|
-
});
|
|
140
|
-
});
|
|
141
|
-
//# sourceMappingURL=history.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"history.test.js","sourceRoot":"","sources":["../../src/test/history.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ingest.test.d.ts","sourceRoot":"","sources":["../../src/test/ingest.test.ts"],"names":[],"mappings":""}
|