memhouse 0.16.0 → 0.18.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 (51) hide show
  1. package/README.md +97 -31
  2. package/bin/memhouse.js +657 -187
  3. package/editors/antigravity.js +2 -0
  4. package/editors/claude.js +3 -1
  5. package/editors/codebuff.js +2 -0
  6. package/editors/codex.js +4 -1
  7. package/editors/commandcode.js +2 -1
  8. package/editors/copilot-jetbrains.js +2 -1
  9. package/editors/copilot.js +2 -1
  10. package/editors/cursor-agent.js +2 -1
  11. package/editors/cursor.js +2 -0
  12. package/editors/gemini.js +2 -1
  13. package/editors/goose.js +2 -1
  14. package/editors/index.js +8 -1
  15. package/editors/kiro.js +3 -2
  16. package/editors/opencode.js +2 -1
  17. package/editors/scope.js +126 -0
  18. package/editors/vscode.js +2 -0
  19. package/editors/zed.js +2 -1
  20. package/memhouse/DESIGN.md +31 -8
  21. package/memhouse/channel.js +50 -0
  22. package/memhouse/delivery/AGENT-INSTALL-PROMPT.md +4 -3
  23. package/memhouse/delivery/AGENT-INSTALL.md +3 -3
  24. package/memhouse/delivery/INVITATION.md.tpl +130 -0
  25. package/memhouse/delivery/PROMPT.md +21 -14
  26. package/memhouse/delivery/kernel-install.md +4 -2
  27. package/memhouse/delivery/plugin/.claude-plugin/plugin.json +2 -2
  28. package/memhouse/delivery/plugin/reference/HOUSE.md +173 -0
  29. package/memhouse/delivery/plugin/skills/access/SKILL.md +148 -0
  30. package/memhouse/delivery/plugin/skills/admin/SKILL.md +30 -1
  31. package/memhouse/delivery/plugin/skills/house/SKILL.md +99 -0
  32. package/memhouse/delivery/plugin/skills/recall/SKILL.md +119 -0
  33. package/memhouse/delivery/plugin/skills/sql/SKILL.md +73 -194
  34. package/memhouse/flags.js +6 -4
  35. package/memhouse/house/HOUSE.md +25 -11
  36. package/memhouse/house/house.js +110 -27
  37. package/memhouse/house/migrate.js +7 -6
  38. package/memhouse/house/relocate.js +2 -2
  39. package/memhouse/house/schema.sql.tpl +3 -3
  40. package/memhouse/provision.js +100 -0
  41. package/memhouse/share.js +64 -0
  42. package/memhouse/shipper/ship.js +87 -28
  43. package/package.json +1 -1
  44. package/memhouse/delivery/plugin/skills/ask/SKILL.md +0 -131
  45. package/memhouse/delivery/plugin/skills/hello/SKILL.md +0 -121
  46. package/memhouse/delivery/plugin/skills/invite/SKILL.md +0 -130
  47. package/memhouse/delivery/plugin/skills/search/SKILL.md +0 -186
  48. package/memhouse/delivery/plugin/skills/sessions/SKILL.md +0 -164
  49. package/memhouse/delivery/plugin/skills/share/SKILL.md +0 -130
  50. package/memhouse/delivery/plugin/skills/status/SKILL.md +0 -139
  51. package/memhouse/delivery/plugin/skills/users/SKILL.md +0 -97
@@ -0,0 +1,130 @@
1
+ # You have been invited to a memhouse
2
+
3
+ {{INVITER}} set up a **house** — a ClickHouse database where a team keeps its coding-agent
4
+ memory — and made you a member of it. This page walks you from the file you were sent to a
5
+ working install, and shows what you get.
6
+
7
+ You received two things:
8
+
9
+ | file | what it is |
10
+ |---|---|
11
+ | `{{FILE}}` | **your credential.** One-time: install consumes it and deletes it. Treat it like a password until then. |
12
+ | this page | the steps. Nothing secret in here. |
13
+
14
+ Do it yourself, or hand this page to your coding agent. From the directory holding both
15
+ files:
16
+
17
+ ```
18
+ claude "Read MEMHOUSE-INVITATION.md and follow it. Ask me before each step that changes this machine."
19
+ ```
20
+
21
+ Any agent that can run shell commands works the same way; the rules it must keep are at
22
+ the end of this page.
23
+
24
+ ## 1. Install memhouse
25
+
26
+ {{INSTALL}}
27
+
28
+ On most Linux distros the global install needs `sudo`; if `npm` says `EACCES`, that is why.
29
+ `memhouse --version` should print `{{VERSION}}`.
30
+
31
+ ## 2. Join the house
32
+
33
+ From the directory holding `{{FILE}}`:
34
+
35
+ ```
36
+ memhouse install --env {{FILE}}
37
+ ```
38
+
39
+ What that does, in order: connects to `{{URL}}` as `{{NAME}}`, **rotates your password to
40
+ one only your machine knows**, creates your rooms, writes your config to `~/.memhouse/env`,
41
+ deletes `{{FILE}}`, and starts shipping in the background. The password in the file stops
42
+ working the moment install finishes, so a copy left in a chat is dead.
43
+
44
+ ## 3. Check it is working
45
+
46
+ ```
47
+ memhouse status # your rooms, how much is in them, the shipper's pulse
48
+ memhouse doctor # every line a check mark = healthy
49
+ memhouse discover # what this machine ships, and where each editor's sessions were found
50
+ ```
51
+
52
+ `memhouse status` shows a session count that grows for a few minutes on a machine with
53
+ history; the first pass ships everything you have. After that it ships new turns as they
54
+ happen.
55
+
56
+ ## 4. What you own, and who can see it
57
+
58
+ Your memory lives in five tables named for you inside `{{DB}}` — `{{DB}}.{{NAME}}_messages`,
59
+ `{{DB}}.{{NAME}}_sessions`, `{{DB}}.{{NAME}}_tool_calls` and two bookkeeping tables — and you
60
+ hold exactly one grant, on `{{DB}}.{{NAME}}_*`. Housemates cannot read your rooms, list
61
+ them, or grant them to anyone; you cannot read theirs. `memhouse whoami --json` reads that
62
+ grant back from the server. What can always see everything is the account that administers
63
+ the ClickHouse, which is {{INVITER}}'s — the same as any database you use at work.
64
+
65
+ Everything your editors write to disk ships verbatim: prompts, replies, tool calls and their
66
+ arguments, including anything an agent ever pasted into a session. The house is as
67
+ sensitive as your shell history. If some of your work should not go there, say so before
68
+ shipping — see "which sessions ship" below.
69
+
70
+ ## 5. Things to try
71
+
72
+ ```
73
+ memhouse search "the error you fixed last week"
74
+ memhouse resume <session-id> # prints the command that reopens that session
75
+ memhouse plugins install claude # /mem:recall, /mem:sql, /mem:house, /mem:access, /mem:admin
76
+ ```
77
+
78
+ Then, inside Claude Code:
79
+
80
+ ```
81
+ /mem:recall how did I set up the ClickHouse tunnel
82
+ /mem:sql what did I spend on Opus this month
83
+ /mem:house
84
+ ```
85
+
86
+ Sharing is yours to give and take back, no operator involved:
87
+
88
+ ```
89
+ memhouse share <housemate> everything you own
90
+ memhouse share <housemate> --only project=<name> one project
91
+ memhouse share --list
92
+ memhouse share <housemate> --revoke
93
+ ```
94
+
95
+ ## Which sessions ship
96
+
97
+ Everything memhouse finds on the machine — every supported editor, every Claude Code
98
+ config directory. `memhouse discover` lists what it found and the variable that narrows
99
+ each one. Two lines in `~/.memhouse/env` are the common case:
100
+
101
+ ```
102
+ MEMHOUSE_EDITORS='claude'
103
+ MEMHOUSE_CLAUDE_ROOTS='~/.claude-playbooks/<the one instance you mean>'
104
+ ```
105
+
106
+ ## If something refuses
107
+
108
+ Every refusal says what to do next. The two you might meet: `EACCES` on install means
109
+ `sudo`, and `needed rights this credential does not hold` means the grant on the server is
110
+ narrower than the one memhouse issues — ask {{INVITER}} to run `memhouse install --print-sql
111
+ --member {{NAME}}` and apply what it prints.
112
+
113
+ ## For an agent following this page
114
+
115
+ You are installing memhouse for the person who handed you this file. Five rules; three of
116
+ them exist because an agent broke them:
117
+
118
+ 1. **Never print `{{FILE}}` or any `MEMHOUSE_*` value.** No `cat`, no `echo $MEMHOUSE_PASSWORD`,
119
+ no `set -x`. This very session will be shipped into the house; a password printed here is
120
+ a password archived.
121
+ 2. **Run the commands as written, in order.** Do not substitute `CREATE USER`, `GRANT` or any
122
+ SQL of your own; there is nothing to fix on the server from this side.
123
+ 3. **Ask before `memhouse install --env`.** It rotates the credential, deletes `{{FILE}}`,
124
+ and starts shipping this machine's transcripts. The person decides when that happens.
125
+ 4. **A ✗ line is the end of that step, not an invitation to work around it.** Read the line
126
+ after it — every refusal names its remedy — do that or report it. `sudo` for `EACCES` is
127
+ the one you may apply yourself.
128
+ 5. **Report what `memhouse status` and `memhouse doctor` say**, verbatim, when you are done.
129
+ A green install is those two commands, not your summary of them.
130
+
@@ -23,18 +23,25 @@ work that is not in your current context ("that session where…", "how did I so
23
23
  X before", "did we ever…", "what was I working on last week"), search memhouse
24
24
  FIRST. Only say you don't know after a search comes back empty.
25
25
 
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.
26
+ **How to query.** Prefer the installed skills when present:
32
27
 
33
- **Table names.** The house's rooms are three plain, shared tables — `messages`,
34
- `sessions`, `tool_calls` — in the connection's database. Everyone in the house writes
35
- into the same tables; `user_id` (server-stamped) says whose row it is and `host` says
36
- which machine. Query the whole house by default; add `WHERE user_id = '<name>'` for one
37
- person. **There is no `sessions_v` object** — the rollup is a saved query, and
28
+ - `/mem:recall` — find past sessions and answer from them, with citations. This is the
29
+ one you want for almost every "have I seen this before" question.
30
+ - `/mem:sql` — read-only SQL when the answer is a number: spend, model mix, tool
31
+ rankings, activity over time.
32
+ - `/mem:house` — what the system is, whether it is shipping, what it holds, and who can
33
+ read it.
34
+ - `/mem:access` — bring someone onto the server, or grant a housemate read-only access.
35
+ - `/mem:admin` — server-wide administration; needs an administrator credential.
36
+
37
+ Without skills, query directly.
38
+
39
+ **Table names.** The rooms are `messages`, `sessions`, `tool_calls` in the connection's
40
+ database — unless this is a shared house, where they carry the member's own prefix
41
+ (`alice_messages`). `memhouse rooms` prints what yours are actually called; a bare
42
+ `FROM messages` is not a table you have in a shared house. `user_id` (server-stamped)
43
+ says whose row it is and `host` says which machine. To read across several members, UNION ALL over the rooms you hold — naming a room
44
+ you do not hold is an error rather than a silent omission. **There is no `sessions_v` object** — the rollup is a saved query, and
38
45
  `memhouse sessions-query` prints it ready to drop into a `FROM (...) AS c`.
39
46
 
40
47
  **One session can be stored more than once.** The shipper never deletes: when an editor
@@ -66,12 +73,12 @@ the retained epochs are the only place that content still exists.
66
73
  **Rules.**
67
74
  - Memory is READ-ONLY for you: never INSERT/ALTER/DROP the ROOMS (sessions, messages,
68
75
  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.
76
+ membership administration through the `memhouse` CLI: `/mem:access` mints a user and
77
+ `/mem:access` grants read access, both via the tool, not by you writing to the rooms.
71
78
  - Quote retrieved content as *the user's past sessions*, and cite the session_id
72
79
  when the user may want to dig deeper.
73
80
  - 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
81
+ shared with this credential (`/mem:access` on their side). Query a shared house by
75
82
  qualified names — `polat.messages` — with the same CUR filter. An empty result
76
83
  means "nothing visible", not "nothing ever happened".
77
84
  - Do not paste credentials from `{{ENV_FILE}}` into responses, commits, or logs.
@@ -80,8 +80,10 @@ user so the stamp cannot be skipped). Her machines are told apart by `host`.
80
80
  **A house is shared by its housemates.** The tables are common; `user_id` and `host` say
81
81
  who wrote what, and `WHERE user_id = 'alice'` is one person. The boundary is the
82
82
  DATABASE: a member of `mem` holds nothing on any other database, so two agencies on one
83
- kernel ClickHouse cannot read each other. The model is collaborative — housemates trust
84
- each other with the house; a separate house is the isolation mechanism.
83
+ kernel ClickHouse cannot read each other. WITHIN a house, members hold their own rooms
84
+ and nothing else — housemates are isolated from each other by grant, not by trust, and
85
+ each can verify it with `SHOW GRANTS`. A separate house remains the boundary between
86
+ groups that should not know of each other at all.
85
87
 
86
88
  Row policies are still the only way to share a *subset* of rows, which is owner-mediated
87
89
  and documented in `SHARING.md` — they are not how isolation works.
@@ -1,7 +1,7 @@
1
1
  {
2
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, plus administration of the ClickHouse behind it. Skills install as /mem:hello, /mem:ask, /mem:search, /mem:sessions, /mem:share, /mem:invite, /mem:sql, /mem:status, /mem:users, and /mem:admin.",
4
- "version": "0.16.0",
3
+ "description": "Conversation memory for AI agents over the memhouse typed ClickHouse store: recall past sessions and answer from them, run analytics, check the house, bring people in, and administer the server. Skills install as /mem:recall, /mem:sql, /mem:house, /mem:access, and /mem:admin.",
4
+ "version": "0.18.0",
5
5
  "author": {
6
6
  "name": "Ramazan Polat"
7
7
  },
@@ -0,0 +1,173 @@
1
+ # The house — connect, schema, and the three things that bite
2
+
3
+ Read this before writing any query. Every `/mem:*` skill points here rather than
4
+ repeating it, so there is one description of the store and it is either right everywhere
5
+ or wrong everywhere.
6
+
7
+ ---
8
+
9
+ ## What this is
10
+
11
+ A **house** is a ClickHouse **database** — on the user's machine, their server, or a
12
+ hosted ClickHouse; memhouse runs no service of its own. Its **rooms** are tables —
13
+ `sessions`, `messages`, `tool_calls` — and the server stamps who wrote every row. A
14
+ house's grants reach nothing outside it.
15
+
16
+ Every member's rooms are named for them — `mem.alice_messages`, `mem.alice_sessions`,
17
+ `mem.alice_tool_calls` — and one grant covers exactly those: `ON mem.alice_*`. Housemates
18
+ keep their own rooms beside yours and cannot read them, list them, or grant them onward;
19
+ you cannot read theirs. `SHOW GRANTS` shows you the one line you hold. **A bare
20
+ `FROM messages` names nothing you have** — the `q()` below prefixes room names with your
21
+ member name for you, and `memhouse rooms` prints what yours are called.
22
+
23
+ An **operator** owns the ClickHouse itself; an **admin credential** is what proves it.
24
+ `memhouse whoami` says which you are holding.
25
+
26
+ Rows arrive from a **shipper** that parses local transcripts from 17 editors. It only
27
+ ever inserts. Nothing in memhouse runs an LLM over your data — these skills retrieve
28
+ rows, and *you* read them.
29
+
30
+ ---
31
+
32
+ ## Connect
33
+
34
+ **Never print this file or the variables in it.** No `cat "$MH_ENV"`, no
35
+ `env | grep MEMHOUSE`, no `set -x`. Anything printed becomes part of a transcript
36
+ memhouse ships back into the house — the archive is insert-only, so a leaked credential
37
+ cannot be withdrawn.
38
+
39
+ Credentials resolve **flags > exported `MEMHOUSE_*` > `$MEMHOUSE_HOME/env`** (default
40
+ `~/.memhouse/env`) — the same order the CLI uses. The dance below snapshots the
41
+ environment first, because sourcing the file would otherwise clobber an already-exported
42
+ variable.
43
+
44
+ ```bash
45
+ MH_ENV="${MEMHOUSE_HOME:-$HOME/.memhouse}/env"
46
+ _u=${MEMHOUSE_URL-}; _s=${MEMHOUSE_USER-}; _p=${MEMHOUSE_PASSWORD-}; _d=${MEMHOUSE_DB-}
47
+ set -a; [ -f "$MH_ENV" ] && . "$MH_ENV"; set +a
48
+ [ -n "$_u" ] && MEMHOUSE_URL=$_u; [ -n "$_s" ] && MEMHOUSE_USER=$_s
49
+ [ -n "$_p" ] && MEMHOUSE_PASSWORD=$_p; [ -n "$_d" ] && MEMHOUSE_DB=$_d
50
+ if [ -z "${MEMHOUSE_URL:-}" ] || [ -z "${MEMHOUSE_USER:-}" ]; then
51
+ echo "no memhouse house configured — run: memhouse install" >&2; exit 1
52
+ fi
53
+
54
+ q() { # read-only by construction; every read path should use this
55
+ sed -E "s/([[:space:](]|^)(FROM|JOIN)[[:space:]]+(sessions|messages|tool_calls|meta|events)([[:space:];,)]|\$)/\1\2 ${MEMHOUSE_USER}_\3\4/g" \
56
+ | curl -sS --fail-with-body --user "$MEMHOUSE_USER:${MEMHOUSE_PASSWORD:-}" \
57
+ --data-binary @- "$MEMHOUSE_URL/?database=${MEMHOUSE_DB:-$MEMHOUSE_USER}&readonly=1"
58
+ }
59
+ q <<'SQL'
60
+ SELECT 1 FORMAT PrettyCompact
61
+ SQL
62
+ ```
63
+
64
+ `readonly=1` is a ClickHouse-side setting: even a mis-generated `DROP` is refused by the
65
+ server, not by discipline.
66
+
67
+ **The `sed` is what makes every query below work in both layouts** — write room names
68
+ bare and it supplies the prefix when there is one. It rewrites only after `FROM`/`JOIN`,
69
+ so a needle like `LIKE '%messages%'` is untouched, and `FROM yigit_messages` is left
70
+ alone — the room name must follow whitespace, and there is none inside `yigit_messages`.
71
+ With no prefix set it expands to nothing and the query passes through verbatim.
72
+
73
+ It is spelled with `[[:space:]]` classes rather than `\b` because **BSD `sed` does not
74
+ support `\b`**: on macOS the `\b` form matched nothing and sent the query through
75
+ unprefixed. That fails loudly as `UNKNOWN_TABLE` rather than quietly reading wrong rows,
76
+ which is the right way round — but it fails.
77
+
78
+ **Reading a housemate's memory.** A share is `GRANT SELECT ON mem.<them>_*` — their
79
+ rooms, not the database. `SHOW TABLES` lists exactly what you may read: your own rooms plus
80
+ any shared with you. Name a housemate's rooms in full (`FROM yigit_messages`); the `q()`
81
+ rewrite leaves an already-prefixed name alone, since applying yours would point at the
82
+ wrong rooms. Say whose memory an answer came from.
83
+
84
+ ---
85
+
86
+ ## Schema
87
+
88
+ **`messages`** — one row per turn.
89
+
90
+ | column | meaning |
91
+ |---|---|
92
+ | `session_id` | `<source>:<adapter-local id>`, e.g. `claude-code:6b1f…` |
93
+ | `seq` | position in the session, from 0 |
94
+ | `source` | editor: `claude-code`, `codex`, `cursor`, … |
95
+ | `host` | the machine, `<hostname>-<8 hex>` — random per install, not derived |
96
+ | `ts` | when the turn happened (`DateTime64(3,'UTC')`) |
97
+ | `role` | `user`, `assistant`, `system` |
98
+ | `model`, `input_tokens`, `output_tokens`, `cache_read_tokens`, `cache_write_tokens` | usage; **empty for several adapters** — see below |
99
+ | `text` | the turn, **truncated at 50,000 chars** |
100
+ | `text_ngram`, `text_word` | `MATERIALIZED lower(text)`, carrying the text indexes |
101
+ | `project`, `folder` | where it happened; `folder` is what `resume` needs |
102
+ | `user_id` | `MATERIALIZED currentUser()` — stamped by the server, unfakeable |
103
+ | `origin` | `ship` or `import` |
104
+ | `epoch` | which *parse* of the session this row belongs to |
105
+
106
+ **`tool_calls`** — `session_id, seq, idx, source, host, tool_name, args, ts, project,
107
+ folder, origin, epoch, user_id`. `args` is truncated at 20,000 chars. **There is no
108
+ result column** — what a tool *returned* is not stored, and adapters truncate any preview
109
+ of it to 300–500 chars inside `messages.text`.
110
+
111
+ **`sessions`** — one metadata row per session: `name` (first user prompt, 120 chars),
112
+ `source`, `host`, `project`, `folder`, `message_count`, timestamps, `extra`. Deliberately
113
+ **not** epoch-keyed: latest write wins.
114
+
115
+ `memhouse sessions-query` prints a self-contained per-session rollup (it carries its own
116
+ `FINAL`) — prefer it over hand-joining.
117
+
118
+ ---
119
+
120
+ ## The three things that bite
121
+
122
+ ### 1. Epochs — a plain `SELECT` over-counts
123
+
124
+ The shipper never overwrites a diverging parse. When a re-parse comes back shorter, or
125
+ any overlapping turn hashes differently — which is what a **compaction** does to a long
126
+ session — it writes the new parse under `epoch + 1` and keeps the old rows. So the same
127
+ session can be present several times over.
128
+
129
+ Reads must resolve to the current epoch. **Every counting or aggregating query needs
130
+ this**, and the physical table name is the *raw* one, so nothing applies it for you:
131
+
132
+ ```sql
133
+ WHERE origin != 'ship'
134
+ OR (session_id, user_id, epoch) IN (
135
+ SELECT session_id, user_id, max(epoch) FROM messages
136
+ WHERE origin = 'ship' GROUP BY session_id, user_id)
137
+ ```
138
+
139
+ Measured on a real house: omitting it over-counted by **34%**. Drop it deliberately only
140
+ when the question is about what a session said *before* it was compacted — superseded
141
+ epochs are the one place that content still exists.
142
+
143
+ ### 2. Join on `(session_id, user_id)`, never `session_id` alone
144
+
145
+ Two housemates can collide on an adapter-local session id. Every join, every `GROUP BY`
146
+ that identifies a session, takes both columns.
147
+
148
+ ### 3. Escape before splicing, and lowercase the needle
149
+
150
+ Search terms, session ids and user ids are arbitrary text going into single-quoted
151
+ literals. Escape backslashes then single quotes (`\` → `\\`, `'` → `\'`) in **every**
152
+ value. `O'Reilly` otherwise breaks the query, and crafted text could reshape it.
153
+
154
+ The FTS columns are `lower(text)`, so lowercase what you look for:
155
+
156
+ ```sql
157
+ WHERE text_ngram LIKE '%needle%' -- substring, any length
158
+ WHERE hasToken(text_word, 'needle') -- whole word, faster on common terms
159
+ ```
160
+
161
+ ---
162
+
163
+ ## Known gaps — say these out loud rather than reporting a wrong number
164
+
165
+ - **Cost is derived**, from `model` + tokens. Several adapters ship **no tokens at all**
166
+ (`kiro`, `vscode`, `zed`, and `cursor` on one of its two storage paths), so their cost
167
+ reads `$0` — which is *unknown*, not free. `memhouse doctor` names them.
168
+ - **Per-message timestamps are interpolated** for most adapters — only the session bounds
169
+ are real. Hour-of-day and "peak time" charts inherit that.
170
+ - **`text` stops at 50,000 chars**, tool `args` at 20,000. A missing tail is truncation,
171
+ not absence.
172
+ - **Tool *results* are not stored at all.** If the question is "what did that command
173
+ print", the house cannot answer it.
@@ -0,0 +1,148 @@
1
+ ---
2
+ name: access
3
+ description: Bring someone onto this memhouse ClickHouse, or let them read your house — mint a new member with their own house and the env file their install needs, grant a housemate read-only access to yours, list who can currently read it, or revoke. Use when the user says "invite X", "add X to memhouse", "get my friend set up", "share my memory with X", "let X see my sessions", "who can read my memory", "stop sharing with X". Inviting comes BEFORE sharing — you can only grant a user who already exists. Read-only by construction on the sharing side: a share can never let anyone write to or delete your memory.
4
+ user-invocable: true
5
+ argument-hint: "invite <name> | share <name> | list | revoke <name>"
6
+ allowed-tools: Bash
7
+ ---
8
+
9
+ # /mem:access — who exists, and who may read
10
+
11
+ Two things that look alike and are not:
12
+
13
+ - **Invite** mints a ClickHouse *user* and their own *house*. Needs an administrator.
14
+ - **Share** grants an existing user `SELECT` on *your* house. You can do this yourself.
15
+
16
+ **Read `../reference/HOUSE.md` first** for the connection block. One rule dominates
17
+ everything here: **a password must never enter this conversation.** memhouse ships this
18
+ transcript into the house, and the archive is insert-only — a credential pasted here
19
+ cannot be withdrawn.
20
+
21
+ ---
22
+
23
+ ## Invite — mint a member
24
+
25
+ This runs the `memhouse invite` CLI and nothing else. **Do not hand-run `CREATE USER` /
26
+ `GRANT` with an admin password substituted into a command** — that burns the credential
27
+ into a transcript on disk. The CLI passes it to one process and never echoes it.
28
+
29
+ 1. **Check the binary:** `command -v memhouse`. Absent → tell the user to
30
+ `npm install -g memhouse`. There is no in-skill fallback, on purpose.
31
+
32
+ 2. **Gather only two things:** the invitee's **name** (validate `[A-Za-z][A-Za-z0-9_]*`;
33
+ refuse otherwise) and the **URL the invitee will reach the house at** — not localhost,
34
+ which is *their* machine. Do not ask about an admin credential yet; the next step
35
+ usually needs none. Default is their own house — omit `--db`.
36
+
37
+ 3. **Run it from the directory the file should land in** (step 4), bare first:
38
+
39
+ ```
40
+ memhouse invite <name> --url <url> [--db <shared-house>]
41
+ ```
42
+
43
+ `invite written: …` → go to step 4, and no secret touched this conversation.
44
+
45
+ Otherwise read WHICH refusal came back; they need different answers.
46
+
47
+ **"cannot reach `<url>` …"** — the address, not your privileges. A `deploy --local`
48
+ house is loopback-bound on purpose, so no LAN address reaches it and no invitee could
49
+ either. Ask for the address the invitee will actually use; do not retry with a guess.
50
+
51
+ **"`<user>` is a MEMBER of this ClickHouse, not an administrator"** — inviting needs
52
+ `CREATE USER` and `CREATE DATABASE`, which a member deliberately lacks. Ask which case
53
+ applies rather than assuming:
54
+
55
+ - *They run the ClickHouse.* Hand them the line to run THEMSELVES, with
56
+ `--admin-password` left OFF so it is prompted for — out of the process list and out
57
+ of shell history:
58
+
59
+ ```
60
+ memhouse invite <name> --url <url> --admin-user <admin>
61
+ ```
62
+
63
+ **You cannot run that yourself:** the prompt needs a TTY your Bash tool lacks, so it
64
+ refuses with "no TTY to prompt on" — and the fix is NOT `--admin-password`, which
65
+ would put the credential in this transcript.
66
+
67
+ - *Someone else runs it.* They cannot invite, and no flag changes that. Print the
68
+ statements for whoever administers the server — this contacts nothing:
69
+
70
+ ```
71
+ memhouse invite <name> --url <url> --print-sql
72
+ ```
73
+
74
+ **"house `<db>` already exists and holds N messages"** — STOP. That house is somebody's
75
+ memory. Report the row and writer counts and let the user choose: a different handle,
76
+ a different `--db`, or `--adopt` if sharing that house is genuinely the intent. Never
77
+ pass `--adopt` on your own initiative.
78
+
79
+ 4. **Put the file where they can attach it.** The CLI writes `invite-<name>.env` into the
80
+ current directory and the user sends it by hand — so run from their downloads
81
+ directory, or move it there. **Never leave it in a git repo.** Confirm it is present
82
+ and mode `600`, state the full path, and **never print its contents**.
83
+
84
+ 5. **Say whether the house was fresh** — `SELECT count() FROM <db>.messages` reads 0 for a
85
+ new one. Second line of defence behind step 3's refusal.
86
+
87
+ 6. **Write the message the user will send.** `invite` wrote TWO files beside each other:
88
+ `invite-<name>.env` (the credential, one-time) and `MEMHOUSE-INVITATION.md` (the steps,
89
+ nothing secret — install, verify, what they own, things to try). The guide does the
90
+ explaining, so the message is short: what memhouse is in a sentence; that the `.env` IS a
91
+ password (do not forward, do not paste in chat); open `MEMHOUSE-INVITATION.md` and follow
92
+ it. If `memhouse --version` shows a pre-release (a `-` in it), the invitee also needs the
93
+ tarball this build came from (`memhouse nightly --out …`); the guide says so and names it.
94
+
95
+ If they are REPLACING an existing credential, add `memhouse ship --full` — a plain
96
+ incremental pass skips sessions the new house has no record of, so their history would
97
+ not follow them.
98
+
99
+ ---
100
+
101
+ ## Share — let someone read your house
102
+
103
+ Sharing runs the `memhouse share` CLI. **Do not hand-write `GRANT` or `CREATE ROW POLICY`
104
+ for this** — partial sharing has four ways to go wrong quietly, and the command handles
105
+ each one:
106
+
107
+ ```
108
+ memhouse share <user> everything you own
109
+ memhouse share <user> --only project=memhouse just one project
110
+ memhouse share <user> --only session=<id> just one conversation
111
+ memhouse share --list who can read it, and how much
112
+ memhouse share <user> --revoke withdraw
113
+ ```
114
+
115
+ Scopes: `session=`, `project=`, `folder=`, `host=`, `source=`, `since=`, `until=`, and
116
+ they combine — `--only project=memhouse,since=2026-08-01`. Report back the counts it
117
+ prints (`2121 of 22488 rows`), because that is what the person will actually see.
118
+
119
+ **Say what a full share exposes** before running one: every session in the house, from
120
+ every machine, project and editor, including anything ever pasted into one. If that is
121
+ more than the user meant, `--only` is the answer, not a warning.
122
+
123
+ **Never widen without being asked.** Re-running `memhouse share <user>` with no `--only`
124
+ on someone currently scoped turns their partial share into a full one — the command says
125
+ so, and you should surface that line rather than let it pass.
126
+
127
+ ### Why the CLI and not SQL
128
+
129
+ - A permissive catch-all policy for everyone else is the obvious design and it **fails
130
+ open**: measured, a second scoped user saw all 22,500 rows instead of their 1,860.
131
+ - Partial sharing depends on `users_without_row_policies_can_read_rows`, which is server
132
+ config, not a query setting, and whose default has moved between versions. The command
133
+ measures the behaviour before creating the first policy and refuses on a server that
134
+ would blindfold your existing readers.
135
+ - Revoking the grant alone leaves the policies behind, and a later re-share silently
136
+ reinherits the old scope.
137
+ - A policy on `messages` but not `tool_calls` leaks. All three rooms move together.
138
+
139
+ If the command refuses because the server hides rows from unpolicied readers, relay its
140
+ message — a full share still works there; only `--only` is unavailable until an admin
141
+ sets that config.
142
+
143
+ ## Never
144
+
145
+ Never accept a password typed into the conversation, and never print one. Never invent an
146
+ admin credential. Never suggest storing one on disk. Never grant beyond `SELECT` — write
147
+ access to your rooms is not a thing to hand out, and `ALL` would let the other side delete
148
+ your memory. If the binary is absent, stop and say so rather than improvising the SQL.
@@ -8,10 +8,13 @@ allowed-tools: Bash
8
8
 
9
9
  # /mem:admin — operate the house as its administrator
10
10
 
11
- A member owns one database. An **administrator** owns the ClickHouse: every house, every
11
+ A member owns a house, or rooms in one. An **administrator** owns the ClickHouse: every house, every
12
12
  account, every grant. This skill is the second one, and it is the only skill allowed to
13
13
  act outside the caller's own house.
14
14
 
15
+ **Read `../reference/HOUSE.md` first** — connection, schema, and the traps every query
16
+ here inherits.
17
+
15
18
  **An admin password must never enter this conversation.** memhouse ships this transcript
16
19
  into the house you are administering, so a password pasted here is a password published
17
20
  to the archive — and unlike a file you can delete, the archive is insert-only. Resolve
@@ -82,6 +85,32 @@ Reads worth knowing — reach for these before inventing SQL:
82
85
  | running queries | `SELECT query_id, user, elapsed, formatReadableSize(memory_usage) AS mem, substring(query,1,120) AS q FROM system.processes` |
83
86
  | unfinished mutations | `SELECT database, table, mutation_id, command, parts_to_do, latest_fail_reason FROM system.mutations WHERE NOT is_done` |
84
87
 
88
+ ### Sharing on someone else's behalf
89
+
90
+ A member runs `memhouse share` against their own house. An administrator can do it for
91
+ any house — useful when someone asks you to open theirs, or to audit what is open:
92
+
93
+ ```bash
94
+ # what every house has opened up, server-wide
95
+ adm "SELECT database, short_name, table, select_filter FROM system.row_policies ORDER BY database, short_name FORMAT PrettyCompact" "?readonly=1"
96
+ adm "SHOW GRANTS FOR <user>"
97
+ ```
98
+
99
+ To change a share, prefer running the member's own command with their credential over
100
+ hand-writing SQL — the CLI handles the four traps (a permissive catch-all failing open,
101
+ the server setting that decides whether unpolicied readers see anything, revoke leaving
102
+ policies behind, and the three rooms drifting out of step). When you must do it as
103
+ admin, mirror exactly what the CLI does: a policy on **all three rooms** or none, and
104
+ `DROP ROW POLICY` on every room when withdrawing.
105
+
106
+ **Dropping a house does not drop its row policies.** They persist as orphans, and a house
107
+ later recreated under the same name silently inherits them. After any `DROP DATABASE`:
108
+
109
+ ```bash
110
+ adm "SELECT short_name, table FROM system.row_policies WHERE database = '<db>'"
111
+ # then DROP ROW POLICY <name> ON <db>.<table> for each
112
+ ```
113
+
85
114
  **Prefer a CLI verb over raw SQL wherever one exists.** `memhouse invite <name> --url …`
86
115
  provisions a member correctly — right grants, right pin, refuses a house that already
87
116
  holds someone's messages — and it takes `--admin-user`/`--admin-password`, so it works