memhouse 0.9.0 → 0.11.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.
@@ -64,9 +64,10 @@ them free and further along; **bet 3 is the one nobody else attempts.**
64
64
  Physical typed columns (what memory-house derives in views) + `tool_calls`
65
65
  (memory-house has no tool table) + one `extra JSON` escape hatch per room so
66
66
  unnormalized adapter fields are never lost. `ReplacingMergeTree(ingested_at)`;
67
- `messages` keyed `(session_id, user_id, origin, seq)` so a re-ship of a
68
- grown/changed session replaces stale rows (latest-wins) and two housemates' rows
69
- never collapse into one. FTS text indexes built in (CH ≥ 26.2).
67
+ `messages` keyed `(session_id, user_id, origin, epoch, seq)` so a re-ship of a
68
+ grown session replaces stale rows (latest-wins), two housemates' rows never
69
+ collapse into one, and a re-parse that SHRANK or was rewritten lands under a new
70
+ epoch instead of over what it replaces. FTS text indexes built in (CH ≥ 26.2).
70
71
  3. **Kernel-installable agency.** Joining any ClickHouse — a kernel's included — is a
71
72
  database plus a credential: `CREATE USER`, `GRANT ALL ON <house>.* `, done. The
72
73
  shipper is the resident that lands with it and creates its own rooms. Identity is
@@ -116,15 +117,39 @@ Config file: `~/.memhouse/config.json` (`hiddenProjects`, future prefs).
116
117
  - Incremental shipping: read existing `(session_id, last_updated_at, message_count)`
117
118
  (FINAL) and skip chats that haven't grown/changed; a full re-ship must remain
118
119
  safe (ReplacingMergeTree collapses).
119
- - Re-shipping a known session **deletes its message/tool rows first**: `ReplacingMergeTree`
120
- collapses same-key rows but cannot remove a row the new parse no longer produces, so a
121
- shrunken re-parse would leave a stale `seq` tail forever.
122
- - **A mutation predicate must bind the user, never call `currentUser()`.** A mutation does
123
- not evaluate it in the caller's context: measured on ClickHouse 25.11 and chdb, the same
124
- `DELETE` matched 0 rows with `user_id = currentUser()` and 2000 with the value bound —
125
- silently, both times. Read the identity once (`SELECT currentUser()`) and pass it as a
126
- query parameter. This applies to `DELETE`/`ALTER … UPDATE`, not to `SELECT`, where
127
- `currentUser()` behaves as expected.
120
+ - **The shipper is insert-only, and nothing may reintroduce a destructive verb.**
121
+ `ReplacingMergeTree` collapses same-key rows but cannot remove a row the new parse no
122
+ longer produces, so a shrunken re-parse leaves a stale `seq` tail. The shipper used to
123
+ clear the session's rows first; that destroyed content held nowhere else, because
124
+ Claude Code compacts transcripts and deletes them after `cleanupPeriodDays` (30 by
125
+ default), and a shorter re-parse cannot be told apart from a fixed adapter bug. Instead
126
+ `decideEpoch` bumps the session's `epoch` when the new parse is shorter than the stored
127
+ one, or diverges from it at an overlapping `seq` (compared on the stored `line_hash`),
128
+ and writes underneath the old parse rather than over it.
129
+ - **memhouse is an accumulator, not a mirror.** Absence of a session on disk is never a
130
+ signal: retention cleanup, a manual delete, a second laptop, an unmounted folder, a
131
+ locked SQLite file and a thrown adapter all produce the identical observation. A
132
+ session that disappears is never cleared and never re-inserted. **No `sync` and no
133
+ `prune` feature may be built here** — either would destroy the archive by design.
134
+ - Reads see one parse per session. `roomNames()` resolves `messages`/`tool_calls` to a
135
+ current-epoch subquery and the bare tables only as `*_raw`, so retention cannot silently
136
+ inflate a rollup, and a read path written by someone who has never heard of epochs is
137
+ still correct. Measured cost of the filter: **+10 ms** across the dashboard's queries on
138
+ a real 66k-message house (all of them under 200 ms), and **+13–37 ms** on a synthetic
139
+ 1M-message / 20k-session one. The single shape that grows is a one-session read
140
+ (5 ms → 18 ms at 1M rows): the `IN` aggregates the whole room regardless of the outer
141
+ session filter. If that ever matters, the answer is a small materialized current-epoch
142
+ table maintained by the shipper — never a filter each caller has to remember.
143
+ - **A mutation predicate must bind the user, never call `currentUser()`.** No mutation
144
+ survives in the shipper, but the finding outlives it — `migrate-rooms` and `reset` still
145
+ run statements where it applies. A mutation does not evaluate `currentUser()` in the
146
+ caller's context: measured on ClickHouse 25.11 and chdb, the same `DELETE` matched 0
147
+ rows with `user_id = currentUser()` and 2000 with the value bound — silently, both
148
+ times. Read the identity once (`SELECT currentUser()`) and pass it as a query parameter.
149
+ The same trap bites the other way in a rebuild: `user_id` is `MATERIALIZED
150
+ currentUser()`, so an `INSERT SELECT` that does not carry it explicitly (under
151
+ `insert_allow_materialized_columns=1`) restamps every copied row with whoever ran the
152
+ migration.
128
153
  - Costs are computed in the server from `pricing.js` (repo root) over per-model
129
154
  token sums; `<synthetic>` and empty models excluded from model lists.
130
155
 
@@ -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
@@ -45,28 +45,31 @@ From a checkout instead (contributors): `npm install --no-audit --no-fund` in th
45
45
  repo root — nothing compiles — then substitute `node bin/memhouse.js` for `memhouse`
46
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
 
@@ -84,9 +87,11 @@ no longer exists, so anything named here is a single editor's own store (locked
84
87
  running editor, corrupt, or on a schema this parser does not know).
85
88
 
86
89
  `stats` should show one row per source (claude-code, codex, gemini-cli, …) with
87
- non-zero sessions/messages. Or verify by SQL — rooms are named for your ClickHouse
88
- user, and the session rollup is a saved query rather than an object, so
89
- `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`):
90
95
 
91
96
  ```bash
92
97
  curl -s -u "$MEMHOUSE_USER:$MEMHOUSE_PASSWORD" "$MEMHOUSE_URL/?database=$MEMHOUSE_DB" \
@@ -109,9 +114,26 @@ re-ship loop, so this replaces any separate cron. `memhouse stop` ends both.
109
114
  memhouse plugins install claude
110
115
  ```
111
116
 
112
- Installs the memhouse plugin into `<claude-config>/skills/memhouse`, which loads next
113
- session as `memhouse@skills-dir` and exposes `/memhouse:search`, `/memhouse:sessions`
114
- 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
115
121
  `~/.memhouse/env`), with exported `MEMHOUSE_*` vars taking precedence, and refuse to run
116
122
  rather than guessing a URL when neither is set. `memhouse plugins list` shows what is
117
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.
@@ -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.