memhouse 0.8.0 → 0.11.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.
Files changed (36) hide show
  1. package/README.md +84 -47
  2. package/bin/memhouse.js +993 -89
  3. package/editors/antigravity.js +7 -15
  4. package/editors/cursor.js +18 -24
  5. package/editors/goose.js +9 -21
  6. package/editors/index.js +19 -70
  7. package/editors/opencode.js +6 -15
  8. package/editors/sqlite.js +74 -0
  9. package/editors/windsurf.js +3 -3
  10. package/editors/zed.js +10 -15
  11. package/memhouse/COMPETITION.md +9 -5
  12. package/memhouse/DESIGN.md +37 -12
  13. package/memhouse/RESUME-AND-UPDATE.md +7 -10
  14. package/memhouse/delivery/AGENT-INSTALL-PROMPT.md +10 -2
  15. package/memhouse/delivery/AGENT-INSTALL.md +56 -32
  16. package/memhouse/delivery/PROMPT.md +37 -13
  17. package/memhouse/delivery/kernel-install.md +4 -4
  18. package/memhouse/delivery/plugin/.claude-plugin/plugin.json +14 -5
  19. package/memhouse/delivery/plugin/skills/ask/SKILL.md +112 -0
  20. package/memhouse/delivery/plugin/skills/hello/SKILL.md +76 -0
  21. package/memhouse/delivery/plugin/skills/invite/SKILL.md +65 -0
  22. package/memhouse/delivery/plugin/skills/search/SKILL.md +1 -1
  23. package/memhouse/delivery/plugin/skills/sessions/SKILL.md +3 -3
  24. package/memhouse/delivery/plugin/skills/share/SKILL.md +130 -0
  25. package/memhouse/delivery/plugin/skills/sql/SKILL.md +34 -11
  26. package/memhouse/delivery/plugin/skills/status/SKILL.md +114 -0
  27. package/memhouse/delivery/plugin/skills/users/SKILL.md +97 -0
  28. package/memhouse/house/HOUSE.md +40 -5
  29. package/memhouse/house/house.js +191 -10
  30. package/memhouse/house/migrate.js +277 -0
  31. package/memhouse/house/migrations/0100-epoch-key.js +68 -0
  32. package/memhouse/house/schema.sql.tpl +96 -25
  33. package/memhouse/self-update.js +4 -4
  34. package/memhouse/service.js +7 -2
  35. package/memhouse/shipper/ship.js +471 -131
  36. package/package.json +3 -6
@@ -95,18 +95,15 @@ original to duplicate the copy.
95
95
 
96
96
  ### The gap
97
97
 
98
- `npm i -g memhouse@latest` does one third of the job, and the other two thirds have each
99
- cost a real machine real sessions:
100
-
101
- 1. **`--allow-scripts=better-sqlite3` is not remembered.** Omit it on the upgrade and
102
- `better-sqlite3` has no binding again — cursor, zed, opencode, goose and antigravity
103
- read zero sessions on every pass afterwards.
104
- 2. **The daemons keep running the old code.** See §3.
105
- 3. **The house can be older than the shipper.** A newer shipper may need a column an
98
+ `npm i -g memhouse@latest` does half the job, and the other half has cost a real machine
99
+ real sessions:
100
+
101
+ 1. **The daemons keep running the old code.** See §3.
102
+ 2. **The house can be older than the shipper.** A newer shipper may need a column an
106
103
  existing room lacks; it detects that and prints the rebuild, but only if something runs
107
104
  it.
108
105
 
109
- And from a checkout there is a fourth: `git pull` updates `ui/src` and leaves the built
106
+ And from a checkout there is a third: `git pull` updates `ui/src` and leaves the built
110
107
  bundle in `public/`, so the dashboard serves the previous release however often it is
111
108
  restarted. A published tarball is immune — `prepack` builds `public/` before publish.
112
109
 
@@ -114,7 +111,7 @@ restarted. A published tarball is immune — `prepack` builds `public/` before p
114
111
 
115
112
  | Kind | Detected by | Action |
116
113
  |---|---|---|
117
- | `global` | under `npm prefix -g` | `npm i -g memhouse@latest --allow-scripts=better-sqlite3` |
114
+ | `global` | under `npm prefix -g` | `npm i -g memhouse@latest` |
118
115
  | `checkout` | a `.git` at the root | `git pull --ff-only` → `npm install` → `npm run build` |
119
116
  | `local-dep` | `node_modules/memhouse`, not under the global prefix | refuse; print the right command for where it lives |
120
117
  | `npx` | `_npx` in the resolved path | refuse — the next `npx` resolves the registry anyway |
@@ -42,8 +42,14 @@ already exist.
42
42
  echo a password into the transcript you are writing, which memhouse will then ship.
43
43
  - If a command fails, read what it printed before retrying. memhouse's failures name the
44
44
  cause and usually name the fix — it is not a generic tool that fails generically.
45
- - Rooms are named for the ClickHouse user: `sessions_<them>`, `messages_<them>`,
46
- `tool_calls_<them>`. There are no unsuffixed tables. If you write a query, suffix it.
45
+ - The house's rooms are three PLAIN, SHARED tables — `sessions`, `messages`,
46
+ `tool_calls` — in the connection's database. No suffixes, no per-member tables.
47
+ Direct reads of `messages`/`tool_calls` must filter to the current parse (the shipper
48
+ retains superseded parses under an `epoch` column); the installed skills carry the
49
+ filter, so prefer them for queries.
50
+ - If `ship` refuses with "wrong sorting key … memhouse migrate", the house was built by
51
+ an older memhouse. Run `memhouse migrate` — it copies, swaps atomically, deletes
52
+ nothing, and shipping resumes on its own. Do NOT try to rebuild tables by hand.
47
53
 
48
54
  ## When you are done
49
55
 
@@ -52,6 +58,8 @@ mark. Then tell them:
52
58
 
53
59
  - where the dashboard is (`http://localhost:{{PORT}}`) and that `memhouse start` runs it
54
60
  - that `memhouse search <terms>` searches every past session from every editor
61
+ - that `memhouse plugins install claude` gives their agents /mem:ask, /mem:search,
62
+ /mem:sessions, /mem:share, /mem:sql, /mem:status, /mem:users — offer to run it
55
63
  - that you can now search their history yourself, and offer to try one query
56
64
 
57
65
  If `doctor` reports a failure, say which line failed and what memhouse suggested. Do not
@@ -7,21 +7,21 @@ in order; every command is copy-pasteable.
7
7
  machine it runs on — which editors have sessions, whether a ClickHouse answers, whether
8
8
  docker or podman exists — and states the one route that applies. This file is the static
9
9
  long-form version and assumes a reachable ClickHouse already exists, which is the case
10
- `deploy --local` was added to remove. Prereqs: Node >= 20.19 and a reachable
10
+ `deploy --local` was added to remove. Prereqs: Node >= 24 and a reachable
11
11
  ClickHouse >= 26.2 (local server, Cloud, or a kernel house — see
12
12
  `kernel-install.md` for the kernel path). No checkout required — memhouse is on npm.
13
13
 
14
14
  ## 1. Install
15
15
 
16
16
  ```bash
17
- npm install -g memhouse --allow-scripts=better-sqlite3
17
+ npm install -g memhouse
18
18
  ```
19
19
 
20
- `--allow-scripts=better-sqlite3` is required, not cosmetic. Five adapters (cursor,
21
- goose, opencode, zed, antigravity) read SQLite session stores, and npm 12 blocks
22
- the install script that builds the native binding. Without it those five silently
23
- return zero sessions and you ship a partial history. Verify with step 4 —
24
- `memhouse discover` names any adapter it had to skip.
20
+ No flags. This line used to require `--allow-scripts=better-sqlite3`, without which
21
+ five adapters (cursor, goose, opencode, zed, antigravity) silently returned zero
22
+ sessions and you shipped a partial history. SQLite now comes from `node:sqlite`, which
23
+ is part of Node, so there is no install script and nothing to allow. Step 4 still
24
+ verifies — `memhouse discover` names any adapter it had to skip, for any reason.
25
25
 
26
26
  On `EACCES`, decide by **who owns the prefix root**. Not the path — Homebrew's
27
27
  prefix is `/usr/local` on Intel and `/opt/homebrew` on Apple Silicon and is
@@ -34,7 +34,7 @@ ls -ld "$(npm prefix -g)" # the prefix ROOT decides
34
34
  ```
35
35
 
36
36
  Prefix root owned by `root` → a system-managed Node; re-run as
37
- `sudo npm install -g memhouse --allow-scripts=better-sqlite3`.
37
+ `sudo npm install -g memhouse`.
38
38
 
39
39
  Prefix root owned by the current user → the prefix is theirs (Homebrew, fnm, nvm,
40
40
  volta), so the root-owned file npm tripped on is a stray from an earlier `sudo npm`
@@ -42,31 +42,34 @@ and another `sudo` adds more. Repair that path instead:
42
42
  `sudo chown -R "$(id -u):$(id -g)" <path npm named>` (same for `~/.npm`).
43
43
 
44
44
  From a checkout instead (contributors): `npm install --no-audit --no-fund` in the
45
- repo root — its `allowScripts` field already covers the binding — then substitute
46
- `node bin/memhouse.js` for `memhouse` below.
45
+ repo root — nothing compiles — then substitute `node bin/memhouse.js` for `memhouse`
46
+ below.
47
47
 
48
- ## 2. Configure the connection
48
+ ## 2 + 3. Configure, create the schema, first ship — one command
49
49
 
50
- Write `~/.memhouse/env` (or run `memhouse install --yes --url … --user … --password
51
- … --db …`, which writes it for you):
50
+ Do NOT hand-write `~/.memhouse/env`. `install` validates the connection, builds the
51
+ rooms, ships, and writes the config LAST — a hand-written file is read as truth by
52
+ every later command even when it is wrong, and an example URL pasted verbatim points
53
+ at somebody's real house.
54
+
55
+ With a credential the user already has on the house:
52
56
 
53
57
  ```bash
54
- mkdir -p ~/.memhouse
55
- cat > ~/.memhouse/env <<'EOF'
56
- MEMHOUSE_URL=http://localhost:8123
57
- MEMHOUSE_USER=memhouse_root
58
- MEMHOUSE_PASSWORD=<credential>
59
- MEMHOUSE_DB=mem
60
- EOF
61
- chmod 600 ~/.memhouse/env
58
+ memhouse install --url <house-url> --user <member> --password '<credential>' --db <house>
62
59
  ```
63
60
 
64
- ## 3. Create the schema and run the first ship
61
+ Or, holding an admin credential, mint the member and the house in one go:
65
62
 
66
63
  ```bash
67
- memhouse install --yes # applies the schema, then runs the first full ship
64
+ memhouse install --url <house-url> \
65
+ --admin-user <admin> --admin-password '<admin-credential>' \
66
+ --db <house> --member <member> --member-password '<credential>'
68
67
  ```
69
68
 
69
+ The admin credential is used once and never stored. Ask the user for values; never
70
+ invent them and never default to localhost:8123 — on many machines that is a real house
71
+ belonging to someone else.
72
+
70
73
  Expected output shape: `[memhouse] shipped N sessions (0 skipped) → M msg rows, T
71
74
  tool rows in Xs` with N in the hundreds on a machine with real agent usage.
72
75
 
@@ -77,14 +80,18 @@ memhouse discover # which adapters were read, and which were SKIPPED
77
80
  memhouse stats # per-source counts now in the house
78
81
  ```
79
82
 
80
- Read `discover` first. It prints one line per editor it found sessions for, and
81
- warns by name about any adapter it had to skip — that warning is the only signal
82
- that step 1's `--allow-scripts` flag was missed and five editors are dark.
83
+ Read `discover` first. It prints one line per editor it found sessions for, and warns
84
+ by name about any adapter it had to skip. On a healthy install that list is empty: the
85
+ one cause that could take five adapters out at once — a missing native SQLite binding —
86
+ no longer exists, so anything named here is a single editor's own store (locked by a
87
+ running editor, corrupt, or on a schema this parser does not know).
83
88
 
84
89
  `stats` should show one row per source (claude-code, codex, gemini-cli, …) with
85
- non-zero sessions/messages. Or verify by SQL — rooms are named for your ClickHouse
86
- user, and the session rollup is a saved query rather than an object, so
87
- `memhouse sessions-query` prints it:
90
+ non-zero sessions/messages. Or verify by SQL — the rooms are plain shared tables
91
+ (`sessions`, `messages`, `tool_calls`) and the session rollup is a saved query rather
92
+ than an object, so `memhouse sessions-query` prints it (it also applies the
93
+ current-parse filter that any hand-written read of `messages` needs — the shipper
94
+ retains superseded parses under an `epoch`):
88
95
 
89
96
  ```bash
90
97
  curl -s -u "$MEMHOUSE_USER:$MEMHOUSE_PASSWORD" "$MEMHOUSE_URL/?database=$MEMHOUSE_DB" \
@@ -107,9 +114,26 @@ re-ship loop, so this replaces any separate cron. `memhouse stop` ends both.
107
114
  memhouse plugins install claude
108
115
  ```
109
116
 
110
- Installs the memhouse plugin into `<claude-config>/skills/memhouse`, which loads next
111
- session as `memhouse@skills-dir` and exposes `/memhouse:search`, `/memhouse:sessions`
112
- and `/memhouse:sql`. They read the connection from `$MEMHOUSE_HOME/env` (default
117
+ Installs the memhouse plugin into `<claude-config>/skills/mem`, which loads next
118
+ session as `mem@skills-dir` and exposes `/mem:ask`, `/mem:search`, `/mem:sessions`,
119
+ `/mem:share`, `/mem:sql`, `/mem:status` and `/mem:users`. They read the connection from
120
+ `$MEMHOUSE_HOME/env` (default
113
121
  `~/.memhouse/env`), with exported `MEMHOUSE_*` vars taking precedence, and refuse to run
114
122
  rather than guessing a URL when neither is set. `memhouse plugins list` shows what is
115
123
  installed, `memhouse plugins remove claude` undoes it.
124
+
125
+ ## 7. Upgrading a machine that already ran memhouse < 0.10
126
+
127
+ The 0.10 schema changed the transcript tables' sorting keys, so the first `ship` against
128
+ an old house refuses and names the fix. Run it:
129
+
130
+ ```bash
131
+ memhouse migrate # copies each room, swaps atomically, keeps <room>_pre_epoch
132
+ ```
133
+
134
+ Nothing is deleted; a service-managed shipper resumes on its next pass by itself.
135
+ `memhouse update` performs this for the user interactively (`--migrate` runs it
136
+ unasked); as an agent, prefer `memhouse update --no-install --migrate` after an
137
+ `npm install -g` you already performed. If OTHER machines still ship into the same
138
+ house with an older memhouse, tell the user to upgrade them promptly — the migration
139
+ output explains why and prints the admin-side alternative.
@@ -9,9 +9,10 @@ long-term conversation memory.
9
9
 
10
10
  You have persistent memory of past agent sessions — every conversation this user
11
11
  has had with coding agents (Claude Code, Codex, Gemini CLI, Cursor, and other
12
- editors), across all of YOUR machines, stored in your own rooms in a ClickHouse
13
- database called **{{DB}}**. Other members of the same house have their own rooms and
14
- you hold no grant on them.
12
+ editors), across all of THEIR machines, stored in a ClickHouse database called
13
+ **{{DB}}** (their "house"). The house outlives the transcripts on disk: editors
14
+ compact and delete local session files after weeks, and the house is then the only
15
+ copy.
15
16
  Connection: read `{{ENV_FILE}}` (`MEMHOUSE_URL/USER/PASSWORD/DB`); query over
16
17
  HTTP with `curl -u "$MEMHOUSE_USER:$MEMHOUSE_PASSWORD" "$MEMHOUSE_URL/?database=$MEMHOUSE_DB"`
17
18
  and always add `SETTINGS final=1, join_use_nulls=1` to reads — the second matters
@@ -22,9 +23,12 @@ work that is not in your current context ("that session where…", "how did I so
22
23
  X before", "did we ever…", "what was I working on last week"), search memhouse
23
24
  FIRST. Only say you don't know after a search comes back empty.
24
25
 
25
- **How to query.** Prefer the installed skills when present: `/memhouse:search`
26
- (full-text over messages), `/memhouse:sessions` (list/filter sessions),
27
- `/memhouse:sql` (free-form read-only SQL). Without skills, query directly.
26
+ **How to query.** Prefer the installed skills when present: `/mem:ask` (answer a
27
+ question from memory, with citations), `/mem:search` (full-text over messages),
28
+ `/mem:sessions` (list/filter sessions), `/mem:sql` (free-form read-only SQL),
29
+ `/mem:status` (what the system holds and its health), `/mem:share` and `/mem:users`
30
+ (read-only sharing between users), `/mem:invite` (mint a new member — inviting comes
31
+ before sharing), `/mem:hello` (introduce the system). Without skills, query directly.
28
32
 
29
33
  **Table names.** The house's rooms are three plain, shared tables — `messages`,
30
34
  `sessions`, `tool_calls` — in the connection's database. Everyone in the house writes
@@ -33,21 +37,41 @@ which machine. Query the whole house by default; add `WHERE user_id = '<name>'`
33
37
  person. **There is no `sessions_v` object** — the rollup is a saved query, and
34
38
  `memhouse sessions-query` prints it ready to drop into a `FROM (...) AS c`.
35
39
 
40
+ **One session can be stored more than once.** The shipper never deletes: when an editor
41
+ compacts or shortens a session, the new parse is written under a higher `epoch` and the
42
+ old one is retained. Every direct read of `messages`/`tool_calls` must filter to the
43
+ current parse, or counts double and a replay interleaves two versions of the session:
44
+
45
+ ```sql
46
+ -- call this CUR below
47
+ WHERE origin != 'ship'
48
+ OR (session_id, user_id, epoch) IN (
49
+ SELECT session_id, user_id, max(epoch) FROM messages
50
+ WHERE origin = 'ship' GROUP BY session_id, user_id)
51
+ ```
52
+
53
+ Drop CUR deliberately only when asked what a session said BEFORE it was compacted —
54
+ the retained epochs are the only place that content still exists.
55
+
36
56
  - Find sessions about a topic (FTS, lowercase your terms):
37
57
  `SELECT DISTINCT session_id, any(project), min(ts) FROM messages
38
- WHERE hasToken(text_word, 'clickhouse') GROUP BY session_id
58
+ WHERE hasToken(text_word, 'clickhouse') AND <CUR> GROUP BY session_id
39
59
  ORDER BY 3 DESC LIMIT 10 SETTINGS final=1, join_use_nulls=1 FORMAT PrettyCompact`
40
60
  - Recent sessions: `SELECT session_id, source, project, started, first_prompt
41
61
  FROM $(memhouse sessions-query) AS c ORDER BY started DESC LIMIT 20 SETTINGS final=1, join_use_nulls=1`
62
+ (the rollup applies the current-parse filter itself)
42
63
  - Replay one session: `SELECT role, text FROM messages
43
- WHERE session_id = '<id>' ORDER BY seq SETTINGS final=1, join_use_nulls=1`
64
+ WHERE session_id = '<id>' AND <CUR> ORDER BY seq SETTINGS final=1, join_use_nulls=1`
44
65
 
45
66
  **Rules.**
46
- - Memory is READ-ONLY for you. Never INSERT/ALTER/DROP — ingestion belongs to the
47
- memhouse shipper alone.
67
+ - Memory is READ-ONLY for you: never INSERT/ALTER/DROP the ROOMS (sessions, messages,
68
+ tool_calls) — ingestion belongs to the memhouse shipper alone. The one exception is
69
+ membership administration through the `memhouse` CLI: `/mem:invite` mints a user and
70
+ `/mem:share` grants read access, both via the tool, not by you writing to the rooms.
48
71
  - Quote retrieved content as *the user's past sessions*, and cite the session_id
49
72
  when the user may want to dig deeper.
50
- - Other members' sessions are invisible to you unless they have granted you their rooms.
51
- You hold no grant on them otherwise, so naming one fails rather than returning nothing.
52
- An empty result means "nothing visible", not "nothing ever happened".
73
+ - Other users' HOUSES (other databases on the same server) are readable only when
74
+ shared with this credential (`/mem:share` on their side). Query a shared house by
75
+ qualified names — `polat.messages` — with the same CUR filter. An empty result
76
+ means "nothing visible", not "nothing ever happened".
53
77
  - Do not paste credentials from `{{ENV_FILE}}` into responses, commits, or logs.
@@ -35,7 +35,7 @@ rotate on first connect (`issue-credential{user:'memhouse_root'}`).
35
35
  ## 2. Owner sets up the house
36
36
 
37
37
  ```bash
38
- npm install -g memhouse --allow-scripts=better-sqlite3
38
+ npm install -g memhouse
39
39
  memhouse install --yes \
40
40
  --url https://<kernel-host>:8443 \
41
41
  --user memhouse_root \
@@ -46,9 +46,9 @@ memhouse install --yes \
46
46
  That writes `~/.memhouse/env`, applies the schema, and runs the first ship.
47
47
  `memhouse start` then keeps it fresh on a 300s loop.
48
48
 
49
- Keep `--allow-scripts=better-sqlite3`: without it npm 12 leaves `better-sqlite3`
50
- with no native binding and the five SQLite-backed adapters ship nothing, silently.
51
- `memhouse discover` names any adapter it had to skip.
49
+ No flags: SQLite comes from `node:sqlite`, so there is no native binding to build
50
+ and no adapter that can go dark for want of one. `memhouse discover` still names any
51
+ adapter it had to skip — now always a single editor's own store.
52
52
 
53
53
  The kernel never runs or reads any of this — it is content-blind; the shipper is
54
54
  the agency's own resident, a deterministic `worker` on a loop (no LLM, not an
@@ -1,8 +1,17 @@
1
1
  {
2
- "name": "memhouse",
3
- "description": "Conversation memory for AI agents over the memhouse typed ClickHouse store: search, browse, and analyze every past session across all 17 supported editors and machines. Skills install as /memhouse:search, /memhouse:sessions, and /memhouse:sql.",
4
- "version": "0.4.6",
5
- "author": { "name": "Ramazan Polat" },
2
+ "name": "mem",
3
+ "description": "Conversation memory for AI agents over the memhouse typed ClickHouse store: ask, search, browse, analyze, check, share, and invite across all 17 supported editors and machines. Skills install as /mem:hello, /mem:ask, /mem:search, /mem:sessions, /mem:share, /mem:invite, /mem:sql, /mem:status, and /mem:users.",
4
+ "version": "0.11.0",
5
+ "author": {
6
+ "name": "Ramazan Polat"
7
+ },
6
8
  "homepage": "https://github.com/agent-realm/memhouse",
7
- "keywords": ["memory", "clickhouse", "search", "sessions", "analytics", "agentlytics"]
9
+ "keywords": [
10
+ "memory",
11
+ "clickhouse",
12
+ "search",
13
+ "sessions",
14
+ "analytics",
15
+ "agentlytics"
16
+ ]
8
17
  }
@@ -0,0 +1,112 @@
1
+ ---
2
+ name: ask
3
+ description: Answer a question FROM conversation memory — retrieve the relevant past sessions out of the memhouse ClickHouse store, read the actual transcripts, and synthesize an answer with citations. Use when the user asks something their past work already answered, e.g. "how did I fix X last time", "what approach did we settle on for Y", "why did we choose Z", "have I solved this error before", "what did the reviewer say about W". This is the retrieve-and-answer skill; /mem:search is find-the-session, /mem:sql is run-a-query.
4
+ user-invocable: true
5
+ argument-hint: "<question about past work>"
6
+ allowed-tools: Bash
7
+ ---
8
+
9
+ # /mem:ask — answer from conversation memory
10
+
11
+ Answer the user's question from what the house holds, not from what you assume. The
12
+ house is the archive that OUTLIVES the transcripts on disk — Claude Code compacts and
13
+ deletes local session files, so for anything older than ~30 days memhouse is the only
14
+ place the answer still exists.
15
+
16
+ The synthesis is yours; the store is not. memhouse itself runs no LLM anywhere in its
17
+ write or read path — this skill retrieves rows and YOU read them. Never present a guess
18
+ as a retrieval: if the house has nothing on the question, say so.
19
+
20
+ ## Connection
21
+
22
+ **Never print this file or the variables in it.** No `cat "$MH_ENV"`, no `env | grep
23
+ MEMHOUSE`, no `set -x` around these commands. Anything you print becomes part of a
24
+ transcript that memhouse itself ships into the house.
25
+
26
+ Credentials resolve as **flags > exported `MEMHOUSE_*` > `$MEMHOUSE_HOME/env`**
27
+ (default `~/.memhouse/env`) — the same order the `memhouse` CLI uses:
28
+
29
+ ```bash
30
+ MH_ENV="${MEMHOUSE_HOME:-$HOME/.memhouse}/env"
31
+ _u=${MEMHOUSE_URL-}; _s=${MEMHOUSE_USER-}; _p=${MEMHOUSE_PASSWORD-}; _d=${MEMHOUSE_DB-}
32
+ set -a; [ -f "$MH_ENV" ] && . "$MH_ENV"; set +a
33
+ [ -n "$_u" ] && MEMHOUSE_URL=$_u; [ -n "$_s" ] && MEMHOUSE_USER=$_s
34
+ [ -n "$_p" ] && MEMHOUSE_PASSWORD=$_p; [ -n "$_d" ] && MEMHOUSE_DB=$_d
35
+ if [ -z "${MEMHOUSE_URL:-}" ] || [ -z "${MEMHOUSE_USER:-}" ]; then
36
+ echo "no memhouse house configured — nothing in $MH_ENV and no MEMHOUSE_URL/MEMHOUSE_USER set." >&2
37
+ echo "Run: memhouse install" >&2
38
+ exit 1
39
+ fi
40
+
41
+ curl -sS --fail-with-body --user "$MEMHOUSE_USER:${MEMHOUSE_PASSWORD:-}" \
42
+ --data-binary @- "$MEMHOUSE_URL/?database=${MEMHOUSE_DB:-mem}&final=1&join_use_nulls=1&readonly=1" <<'SQL'
43
+ <the query>
44
+ FORMAT JSONEachRow
45
+ SQL
46
+ ```
47
+
48
+ `readonly=1` is pinned on every request: this skill never writes.
49
+
50
+ ## The method — retrieve, read, answer, cite
51
+
52
+ **Escaping, before anything touches SQL.** Search terms, session ids and user ids are
53
+ arbitrary text going inside single-quoted literals: escape backslashes then single
54
+ quotes (`\` → `\\`, `'` → `\'`) in every value you splice in. A term like `O'Reilly`
55
+ otherwise breaks the query — and crafted text could reshape it.
56
+
57
+ **1. Extract search terms from the question.** Not the question verbatim — the terms
58
+ that would appear in the ANSWER. "How did I fix the ClickHouse auth error?" → search
59
+ `ClickHouse auth`, `Authentication failed`, `ACCESS_DENIED` — error strings beat
60
+ paraphrases, identifiers beat prose. Run 2–3 term variants; the FTS columns are
61
+ lowercased, so lowercase your needle.
62
+
63
+ ```sql
64
+ SELECT session_id, any(folder) AS folder,
65
+ formatDateTime(max(ts), '%Y-%m-%d') AS day, count() AS hits,
66
+ substring(anyIf(text, positionCaseInsensitive(text, 'NEEDLE') > 0), 1, 300) AS sample
67
+ FROM messages
68
+ WHERE text_ngram LIKE '%needle%'
69
+ GROUP BY session_id ORDER BY max(ts) DESC LIMIT 8
70
+ ```
71
+
72
+ **2. Read the strongest sessions — the actual turns, not just the hit line.** Pull a
73
+ window around the hits so you see the resolution, not the complaint:
74
+
75
+ ```sql
76
+ SELECT seq, role, substring(text, 1, 2000) AS text
77
+ FROM messages
78
+ WHERE session_id = '<sid>' AND user_id = '<uid>'
79
+ AND seq BETWEEN <hit_seq - 3> AND <hit_seq + 12>
80
+ ORDER BY seq
81
+ ```
82
+
83
+ Join on `(session_id, user_id)` always — two housemates can collide on an
84
+ adapter-local session id.
85
+
86
+ **3. Answer, then cite.** Lead with the answer; under it list the sessions it came
87
+ from — `session_id`, date, project — so the user can reopen one (`memhouse resume
88
+ <session_id>` prints the command). Quote the decisive lines verbatim where the exact
89
+ wording matters (error text, a command, a config value).
90
+
91
+ **4. Nothing found is an answer too.** Say what you searched and where the house's
92
+ coverage ends (`SELECT min(ts), max(ts), count() FROM messages`) rather than padding.
93
+
94
+ ## What can bite
95
+
96
+ - **Rows can hold more than one PARSE of a session** (`origin`/`epoch` are in the key —
97
+ the house retains superseded parses instead of deleting them). Counts and rollups must
98
+ filter to the current epoch:
99
+ ```sql
100
+ WHERE origin != 'ship'
101
+ OR (session_id, user_id, epoch) IN (
102
+ SELECT session_id, user_id, max(epoch) FROM messages
103
+ WHERE origin = 'ship' GROUP BY session_id, user_id)
104
+ ```
105
+ For step 2's transcript reads, PREFER the filter as well — an unfiltered read of a
106
+ compacted session interleaves two parses. Drop it deliberately only when the question
107
+ is about what a session said BEFORE it was compacted; the superseded epochs are the
108
+ one place that content still exists.
109
+ - **`text` is truncated at 50,000 chars** and tool args at 20,000 — a missing tail is
110
+ truncation, not absence.
111
+ - The rollup (`sessions_v`) is a saved query, not a table — get it with
112
+ `memhouse sessions-query` if you need per-session totals.
@@ -0,0 +1,76 @@
1
+ ---
2
+ name: hello
3
+ description: Introduce memhouse — what it is, what it remembers, and how to use it from here. Use when the user asks "what is memhouse", "what can my memory do", "how do I use this", invokes /mem:hello, or seems new to the memory system. Also the right first answer when another /mem skill fails because nothing is configured yet.
4
+ user-invocable: true
5
+ argument-hint: ""
6
+ allowed-tools: Bash
7
+ ---
8
+
9
+ # /mem:hello — welcome to your memory
10
+
11
+ **Invoking this skill IS the request. Give the introduction immediately — grounded in
12
+ this machine's actual state, not hypotheticals. Nothing to clarify first.**
13
+
14
+ ## First: find out what state this machine is in
15
+
16
+ RUN this yourself with the Bash tool — do not print it for the user to run, and do not
17
+ show the env values it sources. Read its output, then write the introduction using the
18
+ ACTUAL numbers it returns (interpolate them; never emit the literal "N sessions").
19
+
20
+ ```bash
21
+ MH_ENV="${MEMHOUSE_HOME:-$HOME/.memhouse}/env"
22
+ _u=${MEMHOUSE_URL-}; _s=${MEMHOUSE_USER-}; _p=${MEMHOUSE_PASSWORD-}; _d=${MEMHOUSE_DB-}
23
+ set -a; [ -f "$MH_ENV" ] && . "$MH_ENV"; set +a
24
+ [ -n "$_u" ] && MEMHOUSE_URL=$_u; [ -n "$_s" ] && MEMHOUSE_USER=$_s
25
+ [ -n "$_p" ] && MEMHOUSE_PASSWORD=$_p; [ -n "$_d" ] && MEMHOUSE_DB=$_d
26
+ if [ -n "${MEMHOUSE_URL:-}" ] && [ -n "${MEMHOUSE_USER:-}" ]; then
27
+ curl -sS -m 5 --user "$MEMHOUSE_USER:${MEMHOUSE_PASSWORD:-}" \
28
+ --data-binary "SELECT uniqExact(session_id), count(), formatDateTime(min(ts),'%Y-%m-%d') FROM messages" \
29
+ "$MEMHOUSE_URL/?database=${MEMHOUSE_DB:-mem}&final=1&readonly=1" 2>&1
30
+ else
31
+ echo "NOT_CONFIGURED"
32
+ fi
33
+ ```
34
+
35
+ (Do not print the env values, and do not narrate that you are withholding them — just use them.)
36
+
37
+ ## Then introduce it — three states, three openings
38
+
39
+ **Configured and answering** — open with the numbers: "Your memory holds N sessions
40
+ (M messages) going back to <date> — every conversation you've had with coding agents
41
+ (Claude Code, Codex, Cursor, Gemini CLI and 13 more), across all your machines. It
42
+ outlives the transcripts on disk: editors delete their session files after weeks; this
43
+ doesn't." Then the tour below.
44
+
45
+ **Not configured** — say what memhouse IS (one paragraph: a personal, permanent archive
46
+ of every AI coding session, stored in a ClickHouse "house" you own), then how to start:
47
+ `memhouse onboard` (wizard) or `memhouse install --env <file>` if someone invited them
48
+ (the file came from `/mem:invite` on the inviter's side).
49
+
50
+ **Configured but unreachable** — say which URL refused, point at `memhouse doctor`,
51
+ stop. Never guess another endpoint.
52
+
53
+ ## The tour (keep it to one screen)
54
+
55
+ What you can DO from this chat:
56
+
57
+ - `/mem:ask <question>` — answer from your past work, with citations ("how did I fix X
58
+ last time?")
59
+ - `/mem:search <terms>` — find the session ("that conversation about Y")
60
+ - `/mem:sessions` — browse and filter what's stored
61
+ - `/mem:sql <question or SQL>` — ad-hoc analytics (tokens, costs, rankings)
62
+ - `/mem:status` — is it healthy, how much is stored, which machines ship into it
63
+
64
+ And around other people — **inviting comes before sharing**:
65
+
66
+ - `/mem:invite <name>` — mint a friend's account + their own house on your ClickHouse,
67
+ hand them one env file; they install with it and have their own memory
68
+ - `/mem:share <name>` — once they exist, open a read-only window into YOUR memory
69
+ (`/mem:share revoke <name>` closes it); `/mem:users` shows who's around
70
+
71
+ Also worth naming: `memhouse start` serves a dashboard at http://localhost:4640 for
72
+ browsing and analytics outside the chat, and everything here is read-only for agents —
73
+ only the shipper writes.
74
+
75
+ End by offering one concrete action: a search for something they mentioned recently, or
76
+ `/mem:status` if they seem unsure it's working. One offer, not a menu.
@@ -0,0 +1,65 @@
1
+ ---
2
+ name: invite
3
+ description: Invite another person onto this memhouse ClickHouse — mint their user and their own house (database) with the right grants, producing the one env file their install needs. Use when the user says "invite X", "add X to memhouse", "get my friend set up". Inviting comes BEFORE sharing — /mem:share can only grant a user who exists.
4
+ user-invocable: true
5
+ argument-hint: "<name> [into <db>]"
6
+ allowed-tools: Bash
7
+ ---
8
+
9
+ # /mem:invite — mint a member and hand them one file
10
+
11
+ This runs the `memhouse invite` CLI and nothing else. Inviting mints a ClickHouse user
12
+ and a house, which takes an ADMIN credential — and an admin password must never enter
13
+ this conversation, because memhouse ships the transcript into the house it just
14
+ provisioned. The CLI is the only safe path: the admin password is passed to one process
15
+ and never echoed. **Do not hand-run `CREATE USER` / `GRANT` with an admin password
16
+ substituted into a command — that burns the credential into the transcript on disk.**
17
+
18
+ ## Do this
19
+
20
+ 1. Confirm the `memhouse` binary is present: `command -v memhouse`. If it is not, tell
21
+ the user to install it first (`npm install -g memhouse`) — there is no in-skill
22
+ fallback, on purpose (see above).
23
+
24
+ 2. Gather ONLY two things by asking:
25
+ - the invitee's **name** (validate `[A-Za-z][A-Za-z0-9_]*`; refuse otherwise)
26
+ - the **URL the invitee will reach the house at** — NOT localhost (that is the
27
+ invitee's own machine). A LAN IP, hostname, or tunnel.
28
+
29
+ Do NOT ask for an admin user or password yet — the next step usually needs neither.
30
+ Only ask about a **shared house** if the user brings one up; default is the invitee's
31
+ own house (omit --db; never pass `--db default` for "the default house").
32
+
33
+ 3. TRY IT BARE FIRST — run this via the Bash tool. `memhouse invite` uses the caller's
34
+ OWN configured credential when it can manage users, which is the common case for
35
+ whoever set the house up, so no admin is needed:
36
+
37
+ ```
38
+ memhouse invite <name> --url <url> [--db <shared-house>]
39
+ ```
40
+
41
+ If it prints `invite written: …`, go to step 4 — you are done, and no admin secret
42
+ ever touched this conversation.
43
+
44
+ ONLY if it fails with "your configured credential cannot create users" does
45
+ provisioning need a real admin. Do NOT take the admin password in chat — memhouse
46
+ archives this transcript. Hand the user the line to run THEMSELVES (a leading space
47
+ keeps it out of shell history):
48
+
49
+ ```
50
+ memhouse invite <name> --url <url> [--db <shared-house>] --admin-user <admin> --admin-password 'PASTE_IT_HERE'
51
+ ```
52
+
53
+ 4. When it succeeds it prints `invite written: invite-<name>.env`. Relay to the user:
54
+ - hand `invite-<name>.env` to the invitee over a trusted channel (croc, a password
55
+ manager — not chat); the file IS a password
56
+ - the invitee runs `memhouse install --env invite-<name>.env` — it OFFERS to change
57
+ the password to one only they know (they were granted rotation on their own account),
58
+ then deletes the file; so the password you set stops working once they install
59
+ - once installed, sharing works: `/mem:share <name>` on either side
60
+
61
+ ## Never
62
+
63
+ Never create a ClickHouse user by running raw SQL with an admin password you were given
64
+ in chat. Never invent an admin credential. Never suggest granting beyond what `memhouse
65
+ invite` does. If the binary is absent, stop and say so — do not improvise the SQL.
@@ -6,7 +6,7 @@ argument-hint: "<search terms> [in <project>] [last <N> days] [from <editor>]"
6
6
  allowed-tools: Bash
7
7
  ---
8
8
 
9
- # /memhouse:search — search conversation memory
9
+ # /mem:search — search conversation memory
10
10
 
11
11
  Search the full message history in the memhouse house. Every room is named for your
12
12
  ClickHouse user — `messages`, `sessions` — see **Room names** below.
@@ -6,7 +6,7 @@ argument-hint: "[N] [project <name>] [from <editor>] [today|week|month]"
6
6
  allowed-tools: Bash
7
7
  ---
8
8
 
9
- # /memhouse:sessions — browse session history
9
+ # /mem:sessions — browse session history
10
10
 
11
11
  List/filter sessions from the session rollup — a saved query over your own rooms, not an
12
12
  object; `memhouse sessions-query` prints it (session metadata +
@@ -154,11 +154,11 @@ FORMAT PrettyCompact
154
154
  | "what was I working on yesterday?" | `WHERE ended > now() - INTERVAL 1 DAY` |
155
155
  | "show my last 10 sessions in agentlytics" | `LIMIT 10 … WHERE project ILIKE '%agentlytics%'` |
156
156
  | "list my cursor sessions this month" | `WHERE source = 'cursor' AND ended > now() - INTERVAL 30 DAY` |
157
- | "which machine did I do the schema work on?" | search via /memhouse:search, or filter `host` here |
157
+ | "which machine did I do the schema work on?" | search via /mem:search, or filter `host` here |
158
158
 
159
159
  ## Output
160
160
 
161
161
  A compact table: when, name (or first prompt), source, host, project, msgs,
162
162
  tokens. Offer to pull a full transcript next
163
163
  (`SELECT role, text FROM messages WHERE session_id = '…' ORDER BY seq`) or to
164
- run /memhouse:search for a specific quote.
164
+ run /mem:search for a specific quote.