Structured memory system for AI coding agents. Stores claims about a project (decisions, gotchas, narratives, drift) in a tagged graph, fed by observations from sessions/PRs/code, exposed via CLI and (eventually) an MCP server for Claude Code.
This is Chunk 1 — Foundation. Daemon + CLI + Postgres+pgvector + embeddings client. No write ops yet. See .context/implementation_plan.md for the 5-chunk roadmap and .context/memory_system_design.md for the structural spec.
- Node 20+ (
nvm usepicks up.nvmrc) - Docker (for the default Postgres install — skip if you have local Postgres+pgvector)
- An OpenAI API key
nvm use
npm install
cp .env.example .env # then edit .env to set OPENAI_API_KEY
docker compose up -d pg # Postgres+pgvector on localhost:5434Have your own Postgres+pgvector? Set DATABASE_URL in .env and skip the Docker step.
npm run memory -- ping-embeddings "hello"That's it. On first run the CLI spawns the daemon as a detached background process, the daemon connects to Postgres and applies schema.sql, embeds your text via OpenAI, and the CLI prints the result.
Subsequent commands reuse the running daemon. Daemon logs live at ~/.context-layer/daemon.log.
| Command | What it does |
|---|---|
npm run memory -- ping-embeddings <text> |
Embed text, print vector summary |
npm run memory -- health |
Hit /health on the daemon |
npm run memory -- status |
Daemon up/down, pid, uptime |
npm run memory -- reset --yes |
Drop all tables, reapply schema.sql (destructive) |
npm run memory -- serve |
Run the daemon in the foreground (logs to terminal) |
npm run memory -- stop |
SIGTERM the daemon, clear pidfile |
The CLI auto-starts the daemon when it's down (via a pidfile at ~/.context-layer/daemon.pid and a health poll on 127.0.0.1:8765).
Schema lives in one file: schema.sql at the repo root. Edit freely.
$EDITOR schema.sql # add a column, change a type, drop a table
npm run memory -- reset --yes # drops every table and reapplies schema.sqlData is wiped on every reset. That's deliberate — we'll switch to real migrations once the schema stabilizes (planned for Chunk 4).
npm testThe smoke test spawns a fresh daemon, resets the DB, exercises health/reset/embeddings, and tears down. Runs in <2 seconds (without OpenAI calls) or ~5 seconds (with).
bilbao/
├── schema.sql ← the schema (edit this; memory reset to apply)
├── docker-compose.yml ← pgvector/pgvector:pg16 on port 5434
├── bin/memory.ts ← CLI entrypoint
├── src/
│ ├── config/ ← env validation, constants
│ ├── db/ ← postgres.js client, schema apply/reset
│ ├── embeddings/ ← OpenAI adapter behind an Embedder interface
│ ├── server/ ← Fastify daemon (lifecycle + routes + pidfile)
│ ├── cli/ ← citty commands + daemon auto-start
│ └── util/ ← logger, uuid
└── test/smoke/ ← foundation smoke test
GET /health→{ ok, version, db, uptimeMs, pid }(200 if db up, 503 if down)POST /admin/reset→ drops allpublictables, reappliesschema.sql. Body:{ ok, droppedTables, appliedFrom }POST /embeddings→ body{ text }, response{ dim, embedding }. 400 invalid, 502 embedder failure.
Daemon binds to 127.0.0.1:8765 by default. No auth, no TLS — local-only.
daemon failed to start within 8s — check ~/.context-layer/daemon.log. Usual culprits: Postgres not running (docker compose ps), wrong DATABASE_URL, invalid OPENAI_API_KEY.
Port 8765 in use — set PORT and SERVER_URL in .env.
Schema drift / weird errors after editing schema.sql — npm run memory -- reset --yes.
Stale pidfile — status detects it automatically and clears. If something's truly wedged: rm ~/.context-layer/daemon.pid.