hippocamp 0.1.1 → 0.2.1

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.
@@ -0,0 +1,25 @@
1
+ name: CI
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ pull_request:
6
+ push:
7
+ branches:
8
+ - main
9
+
10
+ permissions:
11
+ contents: read
12
+
13
+ jobs:
14
+ verify:
15
+ runs-on: ubuntu-latest
16
+ steps:
17
+ - uses: actions/checkout@v6
18
+ - uses: actions/setup-node@v6
19
+ with:
20
+ node-version: "24"
21
+ package-manager-cache: false
22
+ - run: npm ci
23
+ - run: node --check scripts/*.cjs
24
+ - run: npm run mcp:help
25
+ - run: npm run mcp:smoke
@@ -0,0 +1,27 @@
1
+ name: Publish Package
2
+
3
+ on:
4
+ release:
5
+ types:
6
+ - published
7
+
8
+ permissions:
9
+ contents: read
10
+ id-token: write
11
+
12
+ jobs:
13
+ publish:
14
+ runs-on: ubuntu-latest
15
+ steps:
16
+ - uses: actions/checkout@v6
17
+ - uses: actions/setup-node@v6
18
+ with:
19
+ node-version: "24"
20
+ registry-url: "https://registry.npmjs.org"
21
+ package-manager-cache: false
22
+ - run: npm ci
23
+ - run: node --check scripts/*.cjs
24
+ - run: npm run mcp:smoke
25
+ - name: Verify release tag matches package version
26
+ run: test "$GITHUB_REF_NAME" = "v$(node -p "require('./package.json').version")"
27
+ - run: npm publish
package/AGENTS.md CHANGED
@@ -22,14 +22,18 @@ This repository is a local-first MCP package for Git-backed agent memory. Keep c
22
22
 
23
23
  ## Repository Map
24
24
 
25
- - `scripts/hippocamp.cjs`: small CLI wrapper for `dream`, `mcp`, `install-codex`, and `install-claude`.
25
+ - `scripts/hippocamp.cjs`: small CLI wrapper for `dream`, `mcp`, `install-codex`, `install-claude`, and `install-grok`.
26
26
  - `scripts/hippocamp-dream.cjs`: offline Dream CLI for scheduled compaction of curated project wake-up files.
27
27
  - `scripts/hippocamp-memory.cjs`: local filesystem-backed Hippocamp memory helpers for MCP use.
28
28
  - `scripts/hippocamp-mcp.cjs`: local stdio MCP server for Hippocamp memory tools.
29
+ - `scripts/eval-retrieval.cjs`: retrieval-only LongMemEval-S check for `searchMemory` against a BM25 reference.
29
30
  - `scripts/install-claude.cjs`: one-step installer for Claude Code MCP setup and user-level `CLAUDE.md` guidance.
30
31
  - `scripts/install-codex.cjs`: one-step installer for the Hippocamp skill and MCP server in Codex.
32
+ - `scripts/install-grok.cjs`: one-step installer for the Hippocamp skill, Grok home rules, and MCP server in Grok Build.
31
33
  - `skills/hippocamp-memory/SKILL.md`: installable skill that tells agents how to use Hippocamp memory.
32
34
  - `assets/github-actions/hippocamp-dream.yml`: scheduled workflow template for Lagoon repos that opens one Dream PR per project.
35
+ - `assets/railway/run-dream.sh`: short-lived hosted Dream runner that clones Lagoon and opens one Dream PR per project.
36
+ - `Dockerfile.railway` and `railway.json`: optional Railway cron deployment for overnight Dream runs.
33
37
  - `assets/brand/hippocamp-mascot.png`: README mascot asset.
34
38
 
35
39
  ## Project Invariants
@@ -41,12 +45,15 @@ This repository is a local-first MCP package for Git-backed agent memory. Keep c
41
45
  - The current project slug is inferred from `HIPPOCAMP_PROJECT_ROOT` or the current working directory.
42
46
  - Event logs are append-only daily Markdown files under `events/YYYY-MM-DD.md`, with sibling `events/YYYY-MM-DD.index.json` cue indexes.
43
47
  - Curated files such as `current_state.md` and `open_threads.md` use explicit file writes.
44
- - Event entries should include concise `Cues:` values for fuzzy recall when possible.
48
+ - MCP `append_event` requires a title and 1-8 cues; MCP `write_memory_file` rejects paths under `events/`; an explicit `projectRoot` must be a directory inside a Git repository.
49
+ - `append_event` auto-stamps `Agent:` / `Session:` provenance when resolvable (installer env, MCP client name, or process session id). No manual user config.
45
50
  - Default sync is automatic through the local Lagoon repo.
46
51
  - Local MCP mode must not require `GITHUB_TOKEN`, GitHub App auth, or a cloud service.
47
52
  - Memory must not duplicate facts already tracked by GitHub commits, PRs, issues, reviews, or CI. Store references plus the missing rationale, preference, assumption, or follow-up context instead.
48
53
  - Dream compaction sends `current_state.md`, `open_threads.md`, and bounded cue-indexed event evidence for open-thread decisions. It must not dump full event logs, cue indexes, or raw GitHub-owned facts into the model prompt.
49
- - The Dream GitHub Actions workflow is a Lagoon repo template. It should remain schedule-only and create reviewable PRs rather than pushing directly to `main`.
54
+ - Dream must compact coherent Markdown snapshots and fail without writing when the snapshot cannot fit. It must not crop wake-up files.
55
+ - The Dream GitHub Actions workflow is a Lagoon repo template. It should remain schedule-only and create PRs rather than pushing directly to `main`. Auto-merge is allowed only for repositories named `lagoon` after exact changed-file validation.
56
+ - The Railway Dream deployment is optional infrastructure. It must exit after each run, keep credentials in environment variables, and preserve the same PR policy.
50
57
 
51
58
  ## Change Rules
52
59
 
@@ -61,14 +68,15 @@ This repository is a local-first MCP package for Git-backed agent memory. Keep c
61
68
  When changing code, use the smallest verification that proves the change:
62
69
 
63
70
  - Run `node --check scripts/*.cjs` for script syntax changes.
71
+ - Run `npm test` for Dream compaction or memory search changes.
64
72
  - Run `npm run mcp:help` when changing MCP registration or commands.
65
73
  - Run `npm run mcp:smoke` when changing memory reads, root resolution, or wake-up behavior.
66
74
  - Run `npm run dream -- --help` when changing Dream CLI arguments.
67
- - There is no dedicated test suite yet, so do not claim test coverage that does not exist.
75
+ - Run `npm run eval:retrieval -- --limit 100` when changing search ranking or snippets; report Recall@5 against the BM25 row.
68
76
 
69
77
  ## Notes For Future Agents
70
78
 
71
79
  - The cloud/API version is intentionally deferred.
72
80
  - Do not reintroduce Next.js, GitHub API token auth, or GitHub App auth for the local MVP.
73
- - Keep Dream minimal: scheduled CLI compaction, current/open curated files plus bounded thread evidence, one PR per project.
81
+ - Keep Dream minimal: scheduled coherent compaction, current/open curated files plus bounded thread evidence, one PR per project.
74
82
  - The local install story should stay: install MCP, point at Lagoon, rely on normal Git auth.
@@ -0,0 +1,15 @@
1
+ FROM node:22-bookworm-slim
2
+
3
+ RUN apt-get update \
4
+ && apt-get install -y --no-install-recommends ca-certificates gh git \
5
+ && rm -rf /var/lib/apt/lists/*
6
+
7
+ WORKDIR /app
8
+
9
+ COPY package.json package-lock.json ./
10
+ RUN npm ci --omit=dev
11
+
12
+ COPY scripts ./scripts
13
+ COPY assets/railway/run-dream.sh ./assets/railway/run-dream.sh
14
+
15
+ CMD ["bash", "assets/railway/run-dream.sh"]
package/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # Hippocamp
2
2
 
3
+ [![CI](https://github.com/guillaumegay13/hippocamp/actions/workflows/ci.yml/badge.svg)](https://github.com/guillaumegay13/hippocamp/actions/workflows/ci.yml)
4
+
3
5
  <p align="center">
4
6
  <img src="./assets/brand/hippocamp-mascot.png" alt="Hippocamp mascot" width="220" />
5
7
  </p>
@@ -45,6 +47,13 @@ npm install
45
47
  npm run install:claude
46
48
  ```
47
49
 
50
+ For Grok Build:
51
+
52
+ ```bash
53
+ npm install
54
+ npm run install:grok
55
+ ```
56
+
48
57
  After publishing, the intended one-line install shape is:
49
58
 
50
59
  ```bash
@@ -56,6 +65,7 @@ Upgrade uses the same install path, so agents can refresh themselves without a s
56
65
  ```bash
57
66
  npx hippocamp@latest upgrade-codex
58
67
  npx hippocamp@latest upgrade-claude
68
+ npx hippocamp@latest upgrade-grok
59
69
  ```
60
70
 
61
71
  From a source checkout:
@@ -65,17 +75,19 @@ git pull --ff-only
65
75
  npm install
66
76
  npm run upgrade:codex
67
77
  npm run upgrade:claude
78
+ npm run upgrade:grok
68
79
  ```
69
80
 
70
- Both installers default to `~/.lagoon` as the memory repo. You can override it:
81
+ Installers default to `~/.lagoon` as the memory repo. You can override it:
71
82
 
72
83
  ```bash
73
84
  npm run install:codex -- --global-root /absolute/path/to/lagoon
74
85
  npm run install:claude -- --global-root /absolute/path/to/lagoon
86
+ npm run install:grok -- --global-root /absolute/path/to/lagoon
75
87
  ```
76
88
 
77
- The Codex installer refreshes `~/.codex/AGENTS.md`. The Claude installer refreshes `~/.claude/CLAUDE.md`.
78
- Both managed instruction blocks tell the agent to call `wake_up` at the start of new top-level coding tasks before repo exploration or edits.
89
+ 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`.
90
+ Managed instruction blocks tell the agent to call `wake_up` at the start of new top-level coding tasks before repo exploration or edits.
79
91
 
80
92
  ## Lagoon Repo
81
93
 
@@ -145,13 +157,13 @@ Typical agent flow:
145
157
 
146
158
  1. Call `wake_up` at the start of a top-level task.
147
159
  2. Read the returned global and project memory.
148
- 3. Use `search_memory` only when wake-up files are not enough.
149
- 4. Use `append_event` for meaningful milestones, with concise cues when possible.
160
+ 3. Use `search_memory` for task-specific recall after wake-up.
161
+ 4. Use `append_event` for meaningful milestones, with a title and 1-8 concise cues.
150
162
  5. Update curated files like `current_state.md` and `open_threads.md` before finishing.
151
163
 
152
164
  Writes sync by default. If sync fails or is skipped, call `sync_memory`.
153
165
 
154
- Event writes update a sibling `events/YYYY-MM-DD.index.json` file. The Markdown event remains the canonical memory; the sidecar keeps cue-based fuzzy search fast and rebuildable.
166
+ Event writes update a sibling `events/YYYY-MM-DD.index.json` file. The Markdown event remains the canonical memory; the sidecar is rebuildable. Search ranks indexed events and curated files with BM25 and typo tolerance ([MiniSearch](https://github.com/lucaong/minisearch)), weighting cues above titles and titles above bodies. It returns small coherent evidence blocks from indexed events. If no indexed result is strong enough, it returns no event result instead of scanning whole event logs.
155
167
 
156
168
  ## Dream
157
169
 
@@ -162,7 +174,9 @@ projects/<project-slug>/current_state.md
162
174
  projects/<project-slug>/open_threads.md
163
175
  ```
164
176
 
165
- It does not dump event logs into the model prompt, does not rewrite append-only events, and does not run during normal MCP wake-up. When a project is over the threshold, Dream builds a capped thread evidence pack by searching cue-indexed project events for `open_threads.md` bullets. The evidence budget follows `--target-chars`, so the same size target controls both the desired output and the supporting context. This gives the model enough context to keep, rewrite, close, or promote threads without loading raw history.
177
+ It does not dump event logs into the model prompt, does not rewrite append-only events, and does not run during normal MCP wake-up. When a project is over the threshold, Dream builds a capped thread evidence pack by searching cue-indexed project events for `open_threads.md` bullets. The evidence budget follows `--target-chars`, so the same size target controls both the desired output and the supporting context.
178
+
179
+ Dream uses an Eve-style compaction loop. It rewrites the complete curated snapshot, measures the result, and compacts that coherent result again when it is still over the target. It never crops a wake-up file. After three passes, it fails without writing either file if the snapshot still does not fit.
166
180
 
167
181
  Dry-run is the default:
168
182
 
@@ -177,21 +191,43 @@ npm run dream -- --project my-project --write
177
191
  - `MANIFEST_API_KEY`
178
192
  - `HIPPOCAMP_DREAM_MODEL` (defaults to `auto`; override if you want a specific provider/model)
179
193
 
180
- The GitHub Actions template at `assets/github-actions/hippocamp-dream.yml` is meant to be copied into the private Lagoon memory repo as `.github/workflows/hippocamp-dream.yml`. It runs on a schedule only, scans projects over the wake-up threshold, and creates or updates one PR per project so each memory compaction is reviewable.
194
+ The GitHub Actions template at `assets/github-actions/hippocamp-dream.yml` is meant to be copied into the private Lagoon memory repo as `.github/workflows/hippocamp-dream.yml`. It runs on a schedule only, scans projects over the wake-up threshold, and creates or updates one PR per project. With `HIPPOCAMP_DREAM_AUTO_MERGE=true`, Dream squash-merges the PR only when the repository name is `lagoon` and the PR changes only the two curated project files.
195
+
196
+ ### Railway
197
+
198
+ Railway can run Dream overnight even when your computer is offline. The included cron service starts once per day at `03:17 UTC`, clones the private Lagoon repo, creates or updates one Dream PR per candidate project, and exits. Set `HIPPOCAMP_DREAM_AUTO_MERGE=true` to enable the same Lagoon-only auto-merge policy.
199
+
200
+ [![Deploy on Railway](https://railway.com/button.svg)](https://railway.com/deploy/hippocamp)
201
+
202
+ Required template variables:
203
+
204
+ - `LAGOON_REPOSITORY`: private memory repo in `owner/repository` form
205
+ - `GITHUB_TOKEN`: fine-grained GitHub token limited to that repo, with read/write access to Contents and Pull requests
206
+ - `MANIFEST_BASE_URL`: Manifest/OpenAI-compatible base URL
207
+ - `MANIFEST_API_KEY`: API key for Dream model requests
208
+
209
+ Optional variables keep the CLI defaults: `HIPPOCAMP_DREAM_MODEL=auto`, `HIPPOCAMP_DREAM_THRESHOLD_CHARS=20000`, and `HIPPOCAMP_DREAM_TARGET_CHARS=15000`. `HIPPOCAMP_DREAM_AUTO_MERGE=true` enables a guarded squash merge for a repository named `lagoon`.
210
+
211
+ Railway is an optional deployment target. Local MCP reads and writes do not use Railway or require these hosted credentials.
181
212
 
182
213
  ## Memory Rules
183
214
 
184
215
  - Keep memory concise.
185
216
  - Prefer curated summaries over raw event history.
186
- - Give events a short `Cues:` section so fuzzy search can recall them without scanning full bodies first.
217
+ - Give events a short `Cues:` section; cues weigh the most in search ranking.
187
218
  - Do not duplicate GitHub-owned facts such as commits, PRs, issues, reviews, or CI results.
188
219
  - Store artifact references plus the missing rationale, preference, assumption, or follow-up context.
189
220
  - Use project scope for project-specific state.
190
221
  - Use global scope only for durable context that should follow you across projects.
191
222
 
223
+ Events are stamped automatically with agent/session provenance when available (`Agent:` / `Session:` lines). Installers set `HIPPOCAMP_AGENT`; session defaults to a process-lifetime id. No manual config is required.
224
+
192
225
  Example event content:
193
226
 
194
227
  ```md
228
+ Agent: claude
229
+ Session: mcp-a1b2c3d4
230
+
195
231
  Cues:
196
232
  - local-first
197
233
  - token-free
@@ -215,12 +251,30 @@ npm run mcp
215
251
  npm run mcp:help
216
252
  npm run mcp:smoke
217
253
  npm run dream
254
+ npm run dream:railway
255
+ npm run eval:retrieval
218
256
  npm run install:codex
219
257
  npm run install:claude
258
+ npm run install:grok
220
259
  npm run upgrade:codex
221
260
  npm run upgrade:claude
261
+ npm run upgrade:grok
222
262
  ```
223
263
 
264
+ `npm run eval:retrieval` measures search on LongMemEval-S without any model: Recall@5, Hit@5, MRR, returned context size, and latency, next to an in-process BM25 reference. Run it with `--help` for the one-time data download.
265
+
266
+ ## Releases
267
+
268
+ Pull requests and pushes to `main` run syntax and MCP smoke checks in GitHub Actions.
269
+
270
+ To publish a package version:
271
+
272
+ 1. Update the version in `package.json` and `package-lock.json`, then merge it to `main`.
273
+ 2. Publish a GitHub Release whose tag is `v<version>`.
274
+ 3. The `Publish Package` workflow verifies the tag, runs the MCP checks, and publishes to npm through trusted publishing.
275
+
276
+ The npm package must trust the GitHub Actions workflow `publish.yml` in `guillaumegay13/hippocamp`. This uses short-lived OIDC credentials instead of an npm token.
277
+
224
278
  ## Configuration
225
279
 
226
280
  Environment variables are optional for local use:
@@ -233,6 +287,7 @@ Environment variables are optional for local use:
233
287
  - `HIPPOCAMP_DREAM_MODEL`: model used by Dream. Default: `auto`
234
288
  - `HIPPOCAMP_DREAM_THRESHOLD_CHARS`: wake-up size required before Dream proposes compaction. Default: `20000`
235
289
  - `HIPPOCAMP_DREAM_TARGET_CHARS`: target combined size for `current_state.md` and `open_threads.md`. Default: `15000`
290
+ - `HIPPOCAMP_DREAM_AUTO_MERGE`: set to `true` for a guarded squash merge in a repository named `lagoon`
236
291
 
237
292
  ## What This Is Not
238
293
 
@@ -22,7 +22,9 @@ jobs:
22
22
  HIPPOCAMP_GLOBAL_ROOT: ${{ github.workspace }}
23
23
  HIPPOCAMP_DREAM_THRESHOLD_CHARS: "20000"
24
24
  HIPPOCAMP_DREAM_TARGET_CHARS: "15000"
25
+ HIPPOCAMP_DREAM_AUTO_MERGE: "true"
25
26
  HIPPOCAMP_DREAM_MODEL: ${{ secrets.HIPPOCAMP_DREAM_MODEL }}
27
+ LAGOON_REPOSITORY: ${{ github.repository }}
26
28
  MANIFEST_BASE_URL: ${{ secrets.MANIFEST_BASE_URL }}
27
29
  MANIFEST_API_KEY: ${{ secrets.MANIFEST_API_KEY }}
28
30
 
@@ -65,6 +67,31 @@ jobs:
65
67
  run: |
66
68
  set -euo pipefail
67
69
 
70
+ enable_lagoon_auto_merge() {
71
+ local project="$1"
72
+ local pr_number="$2"
73
+
74
+ [ "$HIPPOCAMP_DREAM_AUTO_MERGE" = "true" ] || return 0
75
+
76
+ if [ "${LAGOON_REPOSITORY##*/}" != "lagoon" ]; then
77
+ echo "Skipping auto-merge: ${LAGOON_REPOSITORY} is not a repository named lagoon." >&2
78
+ return 0
79
+ fi
80
+
81
+ local changed_path
82
+ while IFS= read -r changed_path; do
83
+ case "$changed_path" in
84
+ "projects/${project}/current_state.md"|"projects/${project}/open_threads.md") ;;
85
+ *)
86
+ echo "Refusing auto-merge: unexpected changed path ${changed_path}." >&2
87
+ return 1
88
+ ;;
89
+ esac
90
+ done < <(git diff --name-only origin/main...HEAD)
91
+
92
+ gh pr merge "$pr_number" --squash --delete-branch
93
+ }
94
+
68
95
  while IFS= read -r project; do
69
96
  [ -n "$project" ] || continue
70
97
 
@@ -113,6 +140,7 @@ jobs:
113
140
  "After:",
114
141
  `- wake_up: ${result.afterWakeUpChars} chars`,
115
142
  `- current_state.md + open_threads.md: ${result.afterBytes} bytes`,
143
+ `- coherent compaction passes: ${result.compactionPasses}`,
116
144
  "",
117
145
  "Policy:",
118
146
  "- touched only `current_state.md` and `open_threads.md`",
@@ -128,10 +156,15 @@ jobs:
128
156
 
129
157
  if [ -n "$existing" ]; then
130
158
  gh pr edit "$existing" --title "$title" --body-file "/tmp/hippocamp-dream-${project}.md"
159
+ pr_number="$existing"
131
160
  else
132
- gh pr create --title "$title" --body-file "/tmp/hippocamp-dream-${project}.md" --head "$branch" --base main
161
+ gh pr create --title "$title" --body-file "/tmp/hippocamp-dream-${project}.md" --head "$branch" --base main >/dev/null
162
+ pr_number="$(gh pr list --head "$branch" --state open --json number --jq '.[0].number // empty')"
133
163
  fi
134
164
 
165
+ [ -n "$pr_number" ] || { echo "Could not resolve Dream PR number for ${project}." >&2; exit 1; }
166
+ enable_lagoon_auto_merge "$project" "$pr_number"
167
+
135
168
  git checkout main
136
169
  git reset --hard origin/main
137
170
  done < /tmp/hippocamp-dream-projects.txt
@@ -0,0 +1,184 @@
1
+ #!/usr/bin/env bash
2
+
3
+ set -euo pipefail
4
+
5
+ required=(
6
+ GITHUB_TOKEN
7
+ LAGOON_REPOSITORY
8
+ MANIFEST_API_KEY
9
+ MANIFEST_BASE_URL
10
+ )
11
+
12
+ for name in "${required[@]}"; do
13
+ if [[ -z "${!name:-}" ]]; then
14
+ echo "Missing required environment variable: ${name}" >&2
15
+ exit 1
16
+ fi
17
+ done
18
+
19
+ if [[ ! "$LAGOON_REPOSITORY" =~ ^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+$ ]]; then
20
+ echo "LAGOON_REPOSITORY must use the owner/repository form." >&2
21
+ exit 1
22
+ fi
23
+
24
+ export GH_TOKEN="$GITHUB_TOKEN"
25
+ export HIPPOCAMP_DREAM_MODEL="${HIPPOCAMP_DREAM_MODEL:-auto}"
26
+ export HIPPOCAMP_DREAM_TARGET_CHARS="${HIPPOCAMP_DREAM_TARGET_CHARS:-15000}"
27
+ export HIPPOCAMP_DREAM_THRESHOLD_CHARS="${HIPPOCAMP_DREAM_THRESHOLD_CHARS:-20000}"
28
+ export HIPPOCAMP_DREAM_AUTO_MERGE="${HIPPOCAMP_DREAM_AUTO_MERGE:-false}"
29
+
30
+ runner_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
31
+ work_root="$(mktemp -d)"
32
+ lagoon_root="$work_root/lagoon"
33
+
34
+ cleanup() {
35
+ rm -rf "$work_root"
36
+ }
37
+
38
+ trap cleanup EXIT
39
+
40
+ gh auth setup-git
41
+ gh repo clone "$LAGOON_REPOSITORY" "$lagoon_root"
42
+
43
+ cd "$lagoon_root"
44
+ git config user.name "${HIPPOCAMP_DREAM_GIT_NAME:-hippocamp-dream[bot]}"
45
+ git config user.email "${HIPPOCAMP_DREAM_GIT_EMAIL:-hippocamp-dream[bot]@users.noreply.github.com}"
46
+
47
+ export HIPPOCAMP_GLOBAL_ROOT="$lagoon_root"
48
+
49
+ node "$runner_root/scripts/hippocamp-dream.cjs" \
50
+ --all \
51
+ --dry-run \
52
+ --json \
53
+ --threshold-chars "$HIPPOCAMP_DREAM_THRESHOLD_CHARS" \
54
+ > "$work_root/scan.json"
55
+
56
+ node - "$work_root/scan.json" > "$work_root/projects.txt" <<'NODE'
57
+ const fs = require("node:fs");
58
+ const scan = JSON.parse(fs.readFileSync(process.argv[2], "utf8"));
59
+ const projects = scan.results
60
+ .filter((item) => item.skipped === "dry_run")
61
+ .map((item) => item.project);
62
+ process.stdout.write(`${projects.join("\n")}${projects.length ? "\n" : ""}`);
63
+ console.error(`Dream candidates: ${projects.length ? projects.join(", ") : "none"}`);
64
+ NODE
65
+
66
+ failed=()
67
+
68
+ enable_lagoon_auto_merge() {
69
+ local project="$1"
70
+ local pr_number="$2"
71
+
72
+ [[ "$HIPPOCAMP_DREAM_AUTO_MERGE" == "true" ]] || return 0
73
+
74
+ if [[ "${LAGOON_REPOSITORY##*/}" != "lagoon" ]]; then
75
+ echo "Skipping auto-merge: ${LAGOON_REPOSITORY} is not a repository named lagoon." >&2
76
+ return 0
77
+ fi
78
+
79
+ local changed_path
80
+ while IFS= read -r changed_path; do
81
+ case "$changed_path" in
82
+ "projects/${project}/current_state.md"|"projects/${project}/open_threads.md") ;;
83
+ *)
84
+ echo "Refusing auto-merge: unexpected changed path ${changed_path}." >&2
85
+ return 1
86
+ ;;
87
+ esac
88
+ done < <(git diff --name-only origin/main...HEAD)
89
+
90
+ gh pr merge "$pr_number" --repo "$LAGOON_REPOSITORY" --squash --delete-branch
91
+ }
92
+
93
+ while IFS= read -r project; do
94
+ [[ -n "$project" ]] || continue
95
+
96
+ branch="dream/${project}"
97
+ result_file="$work_root/${project}.json"
98
+ body_file="$work_root/${project}.md"
99
+
100
+ git fetch origin main
101
+ git checkout -B "$branch" origin/main
102
+
103
+ if ! node "$runner_root/scripts/hippocamp-dream.cjs" \
104
+ --project "$project" \
105
+ --write \
106
+ --json \
107
+ --threshold-chars "$HIPPOCAMP_DREAM_THRESHOLD_CHARS" \
108
+ --target-chars "$HIPPOCAMP_DREAM_TARGET_CHARS" \
109
+ > "$result_file"; then
110
+ echo "Dream failed for ${project}; continuing with remaining projects." >&2
111
+ failed+=("$project")
112
+ git checkout -- .
113
+ continue
114
+ fi
115
+
116
+ if git diff --quiet -- "projects/${project}/current_state.md" "projects/${project}/open_threads.md"; then
117
+ echo "No Dream diff for ${project}"
118
+ continue
119
+ fi
120
+
121
+ git add "projects/${project}/current_state.md" "projects/${project}/open_threads.md"
122
+ git commit -m "Dream compact ${project} memory"
123
+
124
+ if git ls-remote --exit-code --heads origin "refs/heads/${branch}" >/dev/null 2>&1; then
125
+ git fetch origin "refs/heads/${branch}:refs/remotes/origin/${branch}"
126
+ fi
127
+
128
+ git push --force-with-lease origin "HEAD:refs/heads/${branch}"
129
+
130
+ node - "$project" "$result_file" > "$body_file" <<'NODE'
131
+ const fs = require("node:fs");
132
+ const project = process.argv[2];
133
+ const payload = JSON.parse(fs.readFileSync(process.argv[3], "utf8"));
134
+ const result = payload.results[0];
135
+ const lines = [
136
+ `Dream compacted \`${project}\` curated memory.`,
137
+ "",
138
+ "Before:",
139
+ `- wake_up: ${result.wakeUpChars} chars`,
140
+ `- current_state.md: ${result.files["current_state.md"]} bytes`,
141
+ `- open_threads.md: ${result.files["open_threads.md"]} bytes`,
142
+ "",
143
+ "Context:",
144
+ `- open threads found: ${result.context.openThreads}`,
145
+ `- threads with event evidence: ${result.context.threadsWithEvidence}`,
146
+ `- event evidence snippets: ${result.context.evidenceItems}`,
147
+ `- evidence budget used: ${result.context.evidenceChars}/${result.context.evidenceBudgetChars} chars`,
148
+ "",
149
+ "After:",
150
+ `- wake_up: ${result.afterWakeUpChars} chars`,
151
+ `- current_state.md + open_threads.md: ${result.afterBytes} bytes`,
152
+ `- coherent compaction passes: ${result.compactionPasses}`,
153
+ "",
154
+ "Policy:",
155
+ "- touched only `current_state.md` and `open_threads.md`",
156
+ "- used bounded cue-indexed event evidence for thread decisions",
157
+ "- did not modify append-only events or cue indexes",
158
+ "- moved historical detail out of wake-up context",
159
+ ];
160
+ console.log(lines.join("\n"));
161
+ NODE
162
+
163
+ existing="$(gh pr list --repo "$LAGOON_REPOSITORY" --head "$branch" --state open --json number --jq '.[0].number // empty')"
164
+ title="Dream compact ${project} memory"
165
+
166
+ if [[ -n "$existing" ]]; then
167
+ gh api --method PATCH "repos/${LAGOON_REPOSITORY}/pulls/${existing}" \
168
+ --raw-field title="$title" \
169
+ --field "body=@${body_file}" \
170
+ >/dev/null
171
+ pr_number="$existing"
172
+ else
173
+ gh pr create --repo "$LAGOON_REPOSITORY" --title "$title" --body-file "$body_file" --head "$branch" --base main >/dev/null
174
+ pr_number="$(gh pr list --repo "$LAGOON_REPOSITORY" --head "$branch" --state open --json number --jq '.[0].number // empty')"
175
+ fi
176
+
177
+ [[ -n "$pr_number" ]] || { echo "Could not resolve Dream PR number for ${project}." >&2; exit 1; }
178
+ enable_lagoon_auto_merge "$project" "$pr_number"
179
+ done < "$work_root/projects.txt"
180
+
181
+ if ((${#failed[@]})); then
182
+ echo "Dream failed for: ${failed[*]}" >&2
183
+ echo "Exiting 0 so the cron deployment is not marked crashed; failures above are transient per-project model errors." >&2
184
+ fi
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hippocamp",
3
- "version": "0.1.1",
3
+ "version": "0.2.1",
4
4
  "private": false,
5
5
  "description": "Local Git-backed memory for AI agents.",
6
6
  "license": "MIT",
@@ -24,20 +24,27 @@
24
24
  "hippocamp-dream": "scripts/hippocamp-dream.cjs",
25
25
  "hippocamp-mcp": "scripts/hippocamp-mcp.cjs",
26
26
  "hippocamp-install-codex": "scripts/install-codex.cjs",
27
- "hippocamp-install-claude": "scripts/install-claude.cjs"
27
+ "hippocamp-install-claude": "scripts/install-claude.cjs",
28
+ "hippocamp-install-grok": "scripts/install-grok.cjs"
28
29
  },
29
30
  "scripts": {
30
31
  "dream": "node scripts/hippocamp-dream.cjs",
32
+ "dream:railway": "bash assets/railway/run-dream.sh",
33
+ "eval:retrieval": "node scripts/eval-retrieval.cjs",
31
34
  "install:claude": "node scripts/install-claude.cjs",
32
35
  "install:codex": "node scripts/install-codex.cjs",
36
+ "install:grok": "node scripts/install-grok.cjs",
33
37
  "mcp": "node scripts/hippocamp-mcp.cjs",
34
38
  "mcp:help": "node scripts/hippocamp-mcp.cjs --help",
35
39
  "mcp:smoke": "node scripts/hippocamp-mcp.cjs --smoke",
40
+ "test": "node --test test/*.test.cjs",
36
41
  "upgrade:claude": "node scripts/install-claude.cjs",
37
- "upgrade:codex": "node scripts/install-codex.cjs"
42
+ "upgrade:codex": "node scripts/install-codex.cjs",
43
+ "upgrade:grok": "node scripts/install-grok.cjs"
38
44
  },
39
45
  "dependencies": {
40
46
  "@modelcontextprotocol/sdk": "^1.29.0",
47
+ "minisearch": "^7.2.0",
41
48
  "zod": "^4.3.6"
42
49
  }
43
50
  }
package/railway.json ADDED
@@ -0,0 +1,12 @@
1
+ {
2
+ "$schema": "https://railway.com/railway.schema.json",
3
+ "build": {
4
+ "builder": "DOCKERFILE",
5
+ "dockerfilePath": "Dockerfile.railway"
6
+ },
7
+ "deploy": {
8
+ "cronSchedule": "17 3 * * *",
9
+ "restartPolicyType": "NEVER",
10
+ "startCommand": "bash assets/railway/run-dream.sh"
11
+ }
12
+ }