hippocamp 0.2.11 → 0.2.13
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/AGENTS.md +10 -2
- package/README.md +2 -0
- package/docs/development.md +21 -0
- package/docs/how-it-works.md +1 -1
- package/docs/results.md +52 -0
- package/docs/setup.md +1 -1
- package/package.json +2 -1
- package/scripts/hippocamp-memory.cjs +67 -0
- package/scripts/install-claude.cjs +8 -4
- package/scripts/install-common.cjs +30 -1
- package/scripts/eval-lagoon.cjs +0 -135
- package/scripts/eval-qa.cjs +0 -371
- package/scripts/eval-retrieval.cjs +0 -323
- package/test/dream-runner.test.cjs +0 -86
- package/test/dream.test.cjs +0 -190
- package/test/install-common.test.cjs +0 -129
- package/test/mcp-params.test.cjs +0 -80
- package/test/memory-efficiency.test.cjs +0 -718
- package/test/package-paths.test.cjs +0 -42
- package/test/sync-memory.test.cjs +0 -119
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
const assert = require("node:assert/strict");
|
|
2
|
-
const { execFileSync } = require("node:child_process");
|
|
3
|
-
const fs = require("node:fs");
|
|
4
|
-
const path = require("node:path");
|
|
5
|
-
const test = require("node:test");
|
|
6
|
-
|
|
7
|
-
test("published files contain no local home directory paths", () => {
|
|
8
|
-
const root = path.join(__dirname, "..");
|
|
9
|
-
// npm is npm.cmd on Windows, which Node only runs through a shell.
|
|
10
|
-
const output = execFileSync("npm", ["pack", "--dry-run", "--json"], {
|
|
11
|
-
cwd: root,
|
|
12
|
-
encoding: "utf8",
|
|
13
|
-
shell: process.platform === "win32",
|
|
14
|
-
});
|
|
15
|
-
const [pack] = JSON.parse(output);
|
|
16
|
-
const textFiles = pack.files.map((file) => file.path).filter((file) => !file.endsWith(".png"));
|
|
17
|
-
const matches = [];
|
|
18
|
-
|
|
19
|
-
for (const file of textFiles) {
|
|
20
|
-
const content = fs.readFileSync(path.join(root, file), "utf8");
|
|
21
|
-
|
|
22
|
-
for (const [index, line] of content.split("\n").entries()) {
|
|
23
|
-
if (/\/Users\/[A-Za-z0-9._-]+|\/home\/[A-Za-z0-9._-]+/.test(line)) {
|
|
24
|
-
matches.push(`${file}:${index + 1}`);
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
assert.deepEqual(matches, []);
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
test("the README demo GIF stays out of the npm package", () => {
|
|
33
|
-
// The README loads it from GitHub; .npmignore keeps the 600 kB file out of every install.
|
|
34
|
-
const output = execFileSync("npm", ["pack", "--dry-run", "--json"], {
|
|
35
|
-
cwd: path.join(__dirname, ".."),
|
|
36
|
-
encoding: "utf8",
|
|
37
|
-
shell: process.platform === "win32",
|
|
38
|
-
});
|
|
39
|
-
const [pack] = JSON.parse(output);
|
|
40
|
-
|
|
41
|
-
assert.ok(!pack.files.some((file) => file.path.endsWith(".gif")));
|
|
42
|
-
});
|
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
const test = require("node:test");
|
|
2
|
-
const assert = require("node:assert/strict");
|
|
3
|
-
const { execFileSync } = require("node:child_process");
|
|
4
|
-
const fs = require("node:fs/promises");
|
|
5
|
-
const os = require("node:os");
|
|
6
|
-
const path = require("node:path");
|
|
7
|
-
const memory = require("../scripts/hippocamp-memory.cjs");
|
|
8
|
-
|
|
9
|
-
test("sync commits when the Lagoon path goes through a symlink", async (t) => {
|
|
10
|
-
const originalRoot = process.env.HIPPOCAMP_GLOBAL_ROOT;
|
|
11
|
-
const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "hippocamp-symlink-"));
|
|
12
|
-
const realLagoon = path.join(tempRoot, "real-lagoon");
|
|
13
|
-
const linkedLagoon = path.join(tempRoot, "linked-lagoon");
|
|
14
|
-
const projectRoot = path.join(tempRoot, "linked-project");
|
|
15
|
-
const git = (args) => execFileSync("git", args, { cwd: realLagoon }).toString().trim();
|
|
16
|
-
|
|
17
|
-
// CI runners have no Git identity; set one for this test only.
|
|
18
|
-
const identity = { GIT_AUTHOR_NAME: "Test", GIT_AUTHOR_EMAIL: "test@example.com", GIT_COMMITTER_NAME: "Test", GIT_COMMITTER_EMAIL: "test@example.com" };
|
|
19
|
-
const saved = Object.fromEntries(Object.keys(identity).map((key) => [key, process.env[key]]));
|
|
20
|
-
Object.assign(process.env, identity);
|
|
21
|
-
t.after(() => {
|
|
22
|
-
for (const [key, value] of Object.entries(saved)) {
|
|
23
|
-
if (value === undefined) {
|
|
24
|
-
delete process.env[key];
|
|
25
|
-
} else {
|
|
26
|
-
process.env[key] = value;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
await fs.mkdir(realLagoon);
|
|
32
|
-
await fs.symlink(realLagoon, linkedLagoon);
|
|
33
|
-
git(["init", "-q", "-b", "main"]);
|
|
34
|
-
git(["commit", "-q", "--allow-empty", "-m", "init"]);
|
|
35
|
-
process.env.HIPPOCAMP_GLOBAL_ROOT = linkedLagoon;
|
|
36
|
-
t.after(async () => {
|
|
37
|
-
if (originalRoot === undefined) {
|
|
38
|
-
delete process.env.HIPPOCAMP_GLOBAL_ROOT;
|
|
39
|
-
} else {
|
|
40
|
-
process.env.HIPPOCAMP_GLOBAL_ROOT = originalRoot;
|
|
41
|
-
}
|
|
42
|
-
await fs.rm(tempRoot, { recursive: true, force: true, maxRetries: 5 });
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
const result = await memory.appendEvent({
|
|
46
|
-
content: "Use advisory locks.",
|
|
47
|
-
keywords: ["locks"],
|
|
48
|
-
projectRoot,
|
|
49
|
-
scope: "project",
|
|
50
|
-
title: "Queue decision",
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
assert.equal(result.sync.committed, true);
|
|
54
|
-
assert.match(git(["log", "--format=%s", "-1"]), /append project event/);
|
|
55
|
-
|
|
56
|
-
// A symlink inside the Lagoon pointing at another project must not be synced as this one.
|
|
57
|
-
const otherProject = path.join(tempRoot, "other-project");
|
|
58
|
-
await fs.mkdir(path.join(realLagoon, "projects", "other-project"), { recursive: true });
|
|
59
|
-
await fs.symlink(path.join(realLagoon, "projects", "linked-project", "events"), path.join(realLagoon, "projects", "other-project", "events"));
|
|
60
|
-
|
|
61
|
-
await assert.rejects(
|
|
62
|
-
memory.appendEvent({ content: "Cross write.", keywords: ["x"], projectRoot: otherProject, scope: "project", title: "Escape" }),
|
|
63
|
-
/escapes the memory root/,
|
|
64
|
-
);
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
test("sync pushes commits left behind by an earlier failed push", async (t) => {
|
|
68
|
-
const originalRoot = process.env.HIPPOCAMP_GLOBAL_ROOT;
|
|
69
|
-
const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), "hippocamp-backlog-"));
|
|
70
|
-
const remote = path.join(tempRoot, "remote.git");
|
|
71
|
-
const lagoon = path.join(tempRoot, "lagoon");
|
|
72
|
-
const projectRoot = path.join(tempRoot, "project");
|
|
73
|
-
const git = (args, cwd = lagoon) => execFileSync("git", args, { cwd }).toString().trim();
|
|
74
|
-
|
|
75
|
-
const identity = { GIT_AUTHOR_NAME: "Test", GIT_AUTHOR_EMAIL: "test@example.com", GIT_COMMITTER_NAME: "Test", GIT_COMMITTER_EMAIL: "test@example.com" };
|
|
76
|
-
const saved = Object.fromEntries(Object.keys(identity).map((key) => [key, process.env[key]]));
|
|
77
|
-
Object.assign(process.env, identity);
|
|
78
|
-
t.after(() => {
|
|
79
|
-
for (const [key, value] of Object.entries(saved)) {
|
|
80
|
-
if (value === undefined) {
|
|
81
|
-
delete process.env[key];
|
|
82
|
-
} else {
|
|
83
|
-
process.env[key] = value;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
git(["init", "-q", "--bare", "-b", "main", remote], tempRoot);
|
|
89
|
-
git(["clone", "-q", remote, lagoon], tempRoot);
|
|
90
|
-
git(["commit", "-q", "--allow-empty", "-m", "init"]);
|
|
91
|
-
git(["push", "-q", "-u", "origin", "main"]);
|
|
92
|
-
process.env.HIPPOCAMP_GLOBAL_ROOT = lagoon;
|
|
93
|
-
t.after(async () => {
|
|
94
|
-
if (originalRoot === undefined) {
|
|
95
|
-
delete process.env.HIPPOCAMP_GLOBAL_ROOT;
|
|
96
|
-
} else {
|
|
97
|
-
process.env.HIPPOCAMP_GLOBAL_ROOT = originalRoot;
|
|
98
|
-
}
|
|
99
|
-
await fs.rm(tempRoot, { recursive: true, force: true, maxRetries: 5 });
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
// Simulate an unreachable remote for the first write.
|
|
103
|
-
git(["remote", "set-url", "origin", path.join(tempRoot, "missing.git")]);
|
|
104
|
-
const write = await memory.appendEvent({ content: "Use HTTPS.", keywords: ["sync"], projectRoot, scope: "project", title: "Remote" });
|
|
105
|
-
assert.equal(write.sync.committed, true);
|
|
106
|
-
assert.equal(write.sync.pushed, false);
|
|
107
|
-
|
|
108
|
-
git(["remote", "set-url", "origin", remote]);
|
|
109
|
-
const result = await memory.syncMemory({
|
|
110
|
-
scope: "project",
|
|
111
|
-
projectRoot,
|
|
112
|
-
paths: await memory.getDefaultSyncPaths("project", projectRoot),
|
|
113
|
-
message: "sync",
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
assert.equal(result.committed, false);
|
|
117
|
-
assert.equal(result.pushed, true);
|
|
118
|
-
assert.equal(git(["rev-parse", "main"], remote), git(["rev-parse", "HEAD"]));
|
|
119
|
-
});
|