hippocamp 0.2.4 → 0.2.5

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 CHANGED
@@ -8,9 +8,9 @@
8
8
 
9
9
  Agent memory you can `git log`.
10
10
 
11
- Hippocamp gives Claude Code, Codex, and Grok Build one shared memory: plain Markdown in a private Git repo you own. Every session starts by waking up from it, and every decision an agent records is a commit you can read, diff, and revert.
11
+ Hippocamp gives all your coding agents, like Claude Code and Codex, one shared memory: plain Markdown in a private Git repo you own. Every session starts by waking up from it, and every decision an agent records is a commit you can read, diff, and revert.
12
12
 
13
- Open source (MIT). No account, no API key, no cloud service: it runs on your machine and works the moment it is installed. Your memory never leaves your computer except to a Git remote you choose. No database, no vector store, nothing to pay for.
13
+ Open source (MIT). No account, no API key, no Hippocamp server: it runs on your machine and works the moment it is installed. Your memory lives in a private Git repo. No database, no vector store, nothing to pay for.
14
14
 
15
15
  ```bash
16
16
  npx hippocamp@latest install
@@ -22,10 +22,10 @@ This installs Hippocamp into every supported agent CLI it finds, creates `~/.lag
22
22
 
23
23
  Claude Code and Codex now ship their own memory. Use Hippocamp when you want:
24
24
 
25
- - **One memory for every agent.** Claude Code, Codex, and Grok read and write the same repo. Built-in memory stays inside one tool.
25
+ - **One memory for every agent.** Claude Code, Codex, and your other agents read and write the same repo. Built-in memory stays inside one tool.
26
26
  - **History you can audit.** Every memory change is a Git commit with an author, a diff, and a timestamp. Roll back a bad memory like bad code.
27
27
  - **Files you own.** Markdown in your own private repo, readable without any UI, portable across machines through your normal Git credentials.
28
- - **Nothing to sign up for.** Open source, local, free. No account, no API key, no vendor that can change terms or shut down.
28
+ - **Nothing to sign up for.** Open source and free, running on your machine. No account, no API key, no vendor that can change terms or shut down.
29
29
  - **Reviewed compaction.** Optional Dream mode summarizes memory offline and proposes the result as a pull request.
30
30
 
31
31
  ## Results
@@ -111,7 +111,7 @@ npx hippocamp@latest install-grok
111
111
 
112
112
  Upgrade with the same command: `npx hippocamp@latest upgrade`. Installers default to `~/.lagoon` as the memory repo; pass `--global-root /absolute/path/to/lagoon` to use another clone.
113
113
 
114
- Installed through npx, the agent starts the server with `npx -y --prefer-offline hippocamp@<version> mcp`, so it keeps working if npm cleans its cache. From a source checkout, `npm install` then `npm run install:claude` (or `install:codex`, `install:grok`) registers the checkout directly.
114
+ Installed through npx, the installer copies the package to `~/.hippocamp/<version>/` and registers that copy, so the server starts without npx or network and keeps working if npm cleans its cache. From a source checkout, `npm install` then `npm run install:claude` (or `install:codex`, `install:grok`) registers the checkout directly.
115
115
 
116
116
  The Codex installer refreshes `~/.codex/AGENTS.md`. The Claude installer refreshes `~/.claude/CLAUDE.md`. The Grok installer refreshes `~/.grok/rules/hippocamp.md` and registers the MCP server with `HIPPOCAMP_AGENT=grok`.
117
117
  Managed instruction blocks tell the agent to call `wake_up` at the start of new top-level coding tasks before repo exploration or edits.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hippocamp",
3
- "version": "0.2.4",
3
+ "version": "0.2.5",
4
4
  "private": false,
5
5
  "description": "Local Git-backed memory for AI agents.",
6
6
  "license": "MIT",
@@ -64,7 +64,7 @@ async function main() {
64
64
 
65
65
  const server = new McpServer({
66
66
  name: "hippocamp",
67
- version: "0.1.0",
67
+ version: require("../package.json").version,
68
68
  });
69
69
 
70
70
  server.registerTool(
@@ -1,22 +1,31 @@
1
1
  const fsSync = require("node:fs");
2
2
  const fs = require("node:fs/promises");
3
+ const os = require("node:os");
3
4
  const path = require("node:path");
4
5
  const { execFile } = require("node:child_process");
5
6
  const { promisify } = require("node:util");
6
7
 
7
8
  const execFileAsync = promisify(execFile);
8
9
 
9
- // How an agent starts the MCP server. Run through npx, the package lives in npm's cache,
10
- // which npm may clean; a pinned npx command downloads it again instead of breaking.
11
- function mcpServerCommand(repoRoot) {
10
+ // How an agent starts the MCP server. Run through npx, the package sits in npm's cache, which
11
+ // npm may clean. Copy it with its dependencies to ~/.hippocamp/<version> and start it from
12
+ // there: no npx or network at start, so it also works offline.
13
+ function mcpServerCommand(repoRoot, home = os.homedir()) {
12
14
  if (!repoRoot.split(path.sep).includes("_npx")) {
13
15
  return [process.execPath, path.join(repoRoot, "scripts", "hippocamp-mcp.cjs")];
14
16
  }
15
17
 
16
18
  const { version } = require(path.join(repoRoot, "package.json"));
17
- const npx = path.join(path.dirname(process.execPath), process.platform === "win32" ? "npx.cmd" : "npx");
19
+ const target = path.join(home, ".hippocamp", version, "node_modules");
18
20
 
19
- return [fsSync.existsSync(npx) ? npx : path.basename(npx), "-y", "--prefer-offline", `hippocamp@${version}`, "mcp"];
21
+ // Copy into a staging folder first, so a failed copy never removes a working install.
22
+ const staging = `${target}.staging-${process.pid}`;
23
+ fsSync.rmSync(staging, { recursive: true, force: true });
24
+ fsSync.cpSync(path.dirname(repoRoot), staging, { recursive: true, dereference: true });
25
+ fsSync.rmSync(target, { recursive: true, force: true });
26
+ fsSync.renameSync(staging, target);
27
+
28
+ return [process.execPath, path.join(target, path.basename(repoRoot), "scripts", "hippocamp-mcp.cjs")];
20
29
  }
21
30
 
22
31
  async function git(args, cwd) {
@@ -36,13 +36,30 @@ async function tempDir(t, name) {
36
36
  return root;
37
37
  }
38
38
 
39
- test("mcpServerCommand pins npx installs and keeps source checkouts direct", async (t) => {
39
+ test("mcpServerCommand copies npx installs to a stable folder and keeps source checkouts direct", async (t) => {
40
40
  const root = await tempDir(t, "npx");
41
- const npxRoot = path.join(root, "_npx", "abc", "node_modules", "hippocamp");
42
- await fs.mkdir(npxRoot, { recursive: true });
41
+ const modules = path.join(root, "_npx", "abc", "node_modules");
42
+ const npxRoot = path.join(modules, "hippocamp");
43
+ const home = path.join(root, "home");
44
+ await fs.mkdir(path.join(npxRoot, "scripts"), { recursive: true });
45
+ await fs.mkdir(path.join(modules, "minisearch"), { recursive: true });
43
46
  await fs.writeFile(path.join(npxRoot, "package.json"), '{ "version": "9.9.9" }');
47
+ await fs.writeFile(path.join(npxRoot, "scripts", "hippocamp-mcp.cjs"), "// server\n");
48
+ await fs.writeFile(path.join(modules, "minisearch", "index.js"), "// dependency\n");
49
+
50
+ const command = mcpServerCommand(npxRoot, home);
51
+ const stable = path.join(home, ".hippocamp", "9.9.9", "node_modules");
52
+
53
+ assert.deepEqual(command, [process.execPath, path.join(stable, "hippocamp", "scripts", "hippocamp-mcp.cjs")]);
54
+ await fs.stat(command[1]);
55
+ await fs.stat(path.join(stable, "minisearch", "index.js"));
56
+
57
+ // A reinstall of the same version replaces the copy: no stale files, no staging left behind.
58
+ await fs.writeFile(path.join(stable, "stale.js"), "// old\n");
59
+ mcpServerCommand(npxRoot, home);
60
+ await assert.rejects(fs.stat(path.join(stable, "stale.js")));
61
+ assert.deepEqual(await fs.readdir(path.join(home, ".hippocamp", "9.9.9")), ["node_modules"]);
44
62
 
45
- assert.deepEqual(mcpServerCommand(npxRoot).slice(1), ["-y", "--prefer-offline", "hippocamp@9.9.9", "mcp"]);
46
63
  const source = path.join(root, "src", "hippocamp");
47
64
  assert.deepEqual(mcpServerCommand(source), [process.execPath, path.join(source, "scripts", "hippocamp-mcp.cjs")]);
48
65
  });