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
@@ -1,186 +0,0 @@
1
- ---
2
- name: search
3
- description: Full-text search across ALL shipped agent conversations — every session from all 17 supported editors (Claude Code, Codex, Cursor, VS Code, Zed, OpenCode, Gemini CLI, …), every project, every machine — stored in the memhouse ClickHouse. Use whenever the user refers to something from the past that isn't in the current context, e.g. "what did I say about X", "find that conversation about Y", "when did I work on Z", "did I ever try W", "the chat where we discussed it", "remind me how I did it". Searches your own house by default, or a friend's house shared with you when they name it ("find X in yigit's memory"). Reach for this before saying you don't know about prior work.
4
- user-invocable: true
5
- argument-hint: "<search terms> [in <project>] [last <N> days] [from <editor>]"
6
- allowed-tools: Bash
7
- ---
8
-
9
- # /mem:search — search conversation memory
10
-
11
- Search the full message history in the memhouse house. Every room is named for your
12
- ClickHouse user — `messages`, `sessions` — see **Room names** below.
13
-
14
- ## Connection
15
-
16
- **Never print this file or the variables in it.** No `cat "$MH_ENV"`, no `env | grep
17
- MEMHOUSE`, no `set -x` around these commands. Anything you print becomes part of a
18
- transcript that memhouse itself ships into the house.
19
-
20
- Credentials resolve as **flags > exported `MEMHOUSE_*` > `$MEMHOUSE_HOME/env`**
21
- (default `~/.memhouse/env`) — the same order the `memhouse` CLI uses. Every query
22
- runs over ClickHouse HTTP with `final=1` and `join_use_nulls=1` (ReplacingMergeTree
23
- keeps stale row versions until merges; `final=1` collapses to latest-wins — always
24
- include it on reads):
25
-
26
- ```bash
27
- # Config lives at $MEMHOUSE_HOME/env (default ~/.memhouse/env). Exported MEMHOUSE_*
28
- # vars WIN over the file — snapshot them, source, then put them back. Sourcing alone
29
- # lets a stale file silently override the house you were pointed at.
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
- # No default URL. localhost:8123 as memhouse_root is a REAL house on many machines,
36
- # usually the pilot's own — guessing it reads someone else's memory and looks like it
37
- # worked. If there is no config, say so and stop.
38
- if [ -z "${MEMHOUSE_URL:-}" ] || [ -z "${MEMHOUSE_USER:-}" ]; then
39
- # An explicit test, not ${VAR:?msg}: zsh does not expand the message, so under the
40
- # shell Claude Code actually uses the refusal read "nothing in $MH_ENV" literally.
41
- echo "no memhouse house configured — nothing in $MH_ENV and no MEMHOUSE_URL/MEMHOUSE_USER set." >&2
42
- echo "Run: memhouse install" >&2
43
- exit 1
44
- fi
45
-
46
- # --user, not a -K config file. The config-file parser treats `"` and `\` specially, so a
47
- # password containing either authenticates fine from the CLI and fails from every skill
48
- # with "password is incorrect" — measured. The argv exposure -K was meant to avoid is not
49
- # observable here either: curl blanks the --user argument before `ps` can read it (0 hits
50
- # in 50 samples). What actually leaks a credential is PRINTING it, which the rule above
51
- # covers.
52
- curl -sS --fail-with-body --user "$MEMHOUSE_USER:${MEMHOUSE_PASSWORD:-}" \
53
- --data-binary @- "$MEMHOUSE_URL/?database=${MEMHOUSE_DB:-$MEMHOUSE_USER}&final=1&join_use_nulls=1&readonly=1" <<'SQL'
54
- <the query>
55
- FORMAT PrettyCompact
56
- SQL
57
- ```
58
-
59
- `FORMAT PrettyCompact` for display; `FORMAT JSONEachRow` when you want to parse
60
- the rows yourself.
61
-
62
- ## Room names — plain, shared tables
63
-
64
- The house's rooms are three plain tables: `messages`, `sessions`, `tool_calls` — resolved
65
- by the connection's database (`MEMHOUSE_DB`), not by who is asking. Everyone in the house
66
- writes into the same tables; `user_id` (stamped by the server) says whose row it is and
67
- `host` says which machine shipped it. Search the whole house by default; add
68
- `AND user_id = '<name>'` only when the user asks for one person's sessions.
69
-
70
- **Keep the SQL heredoc quoted (`<<'SQL'`).** This skill once told the agent to substitute
71
- a shell variable into the table names, which cannot expand inside a quoted heredoc — so
72
- the agent unquotes it, and then the SEARCH TERMS expand too. Both instances driving this
73
- skill did exactly that, unprompted, on the first attempt. Measured consequences, from
74
- `system.query_log`:
75
-
76
- - a term containing `$home` became the empty string, so `LIKE '%%'` matched **every row**
77
- and reported hits with exit 0 — silently wrong, not an error;
78
- - a term containing a backtick **executed the command inside it**.
79
-
80
- A user's search term is arbitrary text. With the heredoc quoted, neither can happen.
81
-
82
- **There is no `sessions_v` object.** The session rollup is a saved query over those same
83
- rooms — `memhouse sessions-query` prints it for whoever you are connected as, ready to
84
- paste into a `FROM (...) AS c` position.
85
-
86
- **The rollup is self-contained.** As of 0.4.5 the printed text carries its own `FINAL` on
87
- both rooms and a trailing `SETTINGS join_use_nulls = 1`, so it is correct wherever you
88
- paste it. `final=1` on the connection still matters for reads of the rooms THEMSELVES,
89
- which carry no FINAL of their own.
90
-
91
-
92
- ## How to search (the FTS columns)
93
-
94
- `messages` carries two MATERIALIZED lowercase copies of `text`, each with a
95
- text index — search those, display `text`:
96
-
97
- - `text_ngram` (ngram index) → substring match: `text_ngram LIKE '%term%'`
98
- - `text_word` (word index) → whole-token match: `hasToken(text_word, 'term')`
99
-
100
- **Search terms MUST be lowercased** (the columns are `lower(text)`); escape
101
- single quotes in user terms as `\'`. Multiple words → AND of clauses; a quoted
102
- phrase → one `LIKE` clause for the whole phrase.
103
-
104
- ### Example 1 — substring search with session names
105
-
106
- ```sql
107
- SELECT m.ts, m.source, m.project, m.role, m.session_id,
108
- s.name AS session,
109
- substring(m.text, 1, 300) AS snippet
110
- FROM messages AS m
111
- LEFT JOIN sessions AS s USING (session_id, user_id)
112
- WHERE m.text_ngram LIKE '%postgres%' AND m.text_ngram LIKE '%migration%'
113
- ORDER BY m.ts DESC
114
- LIMIT 30
115
- FORMAT PrettyCompact
116
- ```
117
-
118
- ### Example 2 — word match, scoped by editor and time
119
-
120
- ```sql
121
- SELECT ts, project, session_id, substring(text, 1, 200) AS snippet
122
- FROM messages
123
- WHERE hasToken(text_word, 'rls')
124
- AND source = 'claude-code'
125
- AND ts > now() - INTERVAL 14 DAY
126
- ORDER BY ts DESC
127
- LIMIT 20
128
- FORMAT PrettyCompact
129
- ```
130
-
131
- ### Example 3 — which sessions mention it most (then drill in)
132
-
133
- ```sql
134
- SELECT m.session_id, m.user_id, any(s.name) AS session, any(m.project) AS project,
135
- count() AS hits, max(m.ts) AS last_hit
136
- FROM messages AS m
137
- LEFT JOIN sessions AS s USING (session_id, user_id)
138
- WHERE m.text_ngram LIKE '%clickhouse cache%'
139
- GROUP BY m.session_id, m.user_id
140
- ORDER BY hits DESC
141
- LIMIT 10
142
- FORMAT PrettyCompact
143
- ```
144
-
145
- ## Reading another person's house (shared with you)
146
-
147
- Every house is its own ClickHouse database on the same server; a share is a read-only
148
- GRANT on that database. To read a friend's memory you keep YOUR credentials and point the
149
- connection at THEIR house — the rooms (`messages`, `sessions`, `tool_calls`) resolve by
150
- the connection's `database`, so one change redirects every query in this skill.
151
-
152
- 1. **Resolve the target house.** `SHOW DATABASES` lists what your credential may read; the
153
- names that are not `system` / `information_schema` / `default` and not your own
154
- `$MEMHOUSE_DB` are houses shared with you. If the user names one — "yigit's memory",
155
- "in yigit", "house yigit", "from yigit" — set `HOUSE=yigit`; otherwise leave
156
- `HOUSE=$MEMHOUSE_DB`.
157
- 2. **Point the connection at it:** in the recipe above, replace
158
- `database=${MEMHOUSE_DB:-$MEMHOUSE_USER}` with `database=<HOUSE>`. Leave the table names bare —
159
- they resolve to that house. Nothing else in the query changes.
160
- 3. **A house wins over `in <project>`.** A name that matches a readable house is a HOUSE,
161
- not a project filter — otherwise "in yigit" silently filters YOUR house's project
162
- column and returns nothing. If a real project in your own house shares the name, keep
163
- the possessive ("<name>'s memory") or say "project <name>" to force the project reading.
164
- 4. **Read-only, thin by design.** If the SELECT is refused, that house was not shared with
165
- you (or the share was revoked) — say so; never guess another database.
166
-
167
- ## Qualifiers
168
-
169
- - `in <project>` → `AND m.project ILIKE '%<project>%'` — but a name matching a readable
170
- house is a HOUSE (see above), not a project.
171
- - `last N days` → `AND m.ts > now() - INTERVAL N DAY`
172
- - `from <editor>` → `AND m.source = '<id>'` (ids: `claude-code`, `codex`,
173
- `cursor`, `cursor-agent`, `vscode`, `zed`, `opencode`, `gemini-cli`,
174
- `windsurf`, `antigravity`, `copilot-cli`, `goose`, `kiro`, …)
175
- - Skip subagent noise → `AND NOT m.is_subagent`
176
- - Only user/assistant text lives in `messages`; tool invocations are in
177
- `tool_calls` (`tool_name`, `args`) — search `args ILIKE '%term%'` there
178
- only if the user explicitly wants tool calls searched.
179
-
180
- ## Output
181
-
182
- Summarize hits grouped by session (name, source, project, date, 1–2 best
183
- snippets each) and give the total hit count. Offer to pull the full transcript
184
- of the best session (`SELECT role, text FROM messages WHERE session_id = '…'
185
- ORDER BY seq`). If zero hits, retry with broader/fewer terms and report the
186
- corpus size (`SELECT count() FROM messages`).
@@ -1,164 +0,0 @@
1
- ---
2
- name: sessions
3
- description: List recent agent sessions from memhouse conversation memory — names, projects, editors, hosts, times, message and token counts across all 17 supported editors. Use for "what was I working on", "show my recent sessions", "sessions from last week", "what did I do in project X", "list my cursor sessions", or to orient before drilling into one session.
4
- user-invocable: true
5
- argument-hint: "[N] [project <name>] [from <editor>] [today|week|month]"
6
- allowed-tools: Bash
7
- ---
8
-
9
- # /mem:sessions — browse session history
10
-
11
- List/filter sessions from the session rollup — a saved query over your own rooms, not an
12
- object; `memhouse sessions-query` prints it (session metadata +
13
- message aggregates) in the memhouse house.
14
-
15
- ## When to use
16
-
17
- - The user wants an overview of recent activity rather than a specific quote.
18
- - As a first step before digging into one session, when they don't have an
19
- id/name yet.
20
-
21
- ## Connection
22
-
23
- **Never print this file or the variables in it.** No `cat "$MH_ENV"`, no `env | grep
24
- MEMHOUSE`, no `set -x` around these commands. Anything you print becomes part of a
25
- transcript that memhouse itself ships into the house.
26
-
27
- Credentials resolve as **flags > exported `MEMHOUSE_*` > `$MEMHOUSE_HOME/env`**
28
- (default `~/.memhouse/env`) — the same order the `memhouse` CLI uses. Every query
29
- runs over ClickHouse HTTP with `final=1` and `join_use_nulls=1` (ReplacingMergeTree
30
- keeps stale row versions until merges; `final=1` collapses to latest-wins — always
31
- include it on reads):
32
-
33
- ```bash
34
- # Config lives at $MEMHOUSE_HOME/env (default ~/.memhouse/env). Exported MEMHOUSE_*
35
- # vars WIN over the file — snapshot them, source, then put them back. Sourcing alone
36
- # lets a stale file silently override the house you were pointed at.
37
- MH_ENV="${MEMHOUSE_HOME:-$HOME/.memhouse}/env"
38
- _u=${MEMHOUSE_URL-}; _s=${MEMHOUSE_USER-}; _p=${MEMHOUSE_PASSWORD-}; _d=${MEMHOUSE_DB-}
39
- set -a; [ -f "$MH_ENV" ] && . "$MH_ENV"; set +a
40
- [ -n "$_u" ] && MEMHOUSE_URL=$_u; [ -n "$_s" ] && MEMHOUSE_USER=$_s
41
- [ -n "$_p" ] && MEMHOUSE_PASSWORD=$_p; [ -n "$_d" ] && MEMHOUSE_DB=$_d
42
- # No default URL. localhost:8123 as memhouse_root is a REAL house on many machines,
43
- # usually the pilot's own — guessing it reads someone else's memory and looks like it
44
- # worked. If there is no config, say so and stop.
45
- if [ -z "${MEMHOUSE_URL:-}" ] || [ -z "${MEMHOUSE_USER:-}" ]; then
46
- # An explicit test, not ${VAR:?msg}: zsh does not expand the message, so under the
47
- # shell Claude Code actually uses the refusal read "nothing in $MH_ENV" literally.
48
- echo "no memhouse house configured — nothing in $MH_ENV and no MEMHOUSE_URL/MEMHOUSE_USER set." >&2
49
- echo "Run: memhouse install" >&2
50
- exit 1
51
- fi
52
-
53
- # --user, not a -K config file. The config-file parser treats `"` and `\` specially, so a
54
- # password containing either authenticates fine from the CLI and fails from every skill
55
- # with "password is incorrect" — measured. The argv exposure -K was meant to avoid is not
56
- # observable here either: curl blanks the --user argument before `ps` can read it (0 hits
57
- # in 50 samples). What actually leaks a credential is PRINTING it, which the rule above
58
- # covers.
59
- curl -sS --fail-with-body --user "$MEMHOUSE_USER:${MEMHOUSE_PASSWORD:-}" \
60
- --data-binary @- "$MEMHOUSE_URL/?database=${MEMHOUSE_DB:-$MEMHOUSE_USER}&final=1&join_use_nulls=1&readonly=1" <<'SQL'
61
- <the query>
62
- FORMAT PrettyCompact
63
- SQL
64
- ```
65
-
66
- ## Room names — plain, shared tables
67
-
68
- The house's rooms are three plain tables: `messages`, `sessions`, `tool_calls` — resolved
69
- by the connection's database (`MEMHOUSE_DB`), not by who is asking. Everyone in the house
70
- writes into the same tables; `user_id` (stamped by the server) says whose row it is and
71
- `host` says which machine shipped it. Search the whole house by default; add
72
- `AND user_id = '<name>'` only when the user asks for one person's sessions.
73
-
74
- **Keep the SQL heredoc quoted (`<<'SQL'`).** This skill once told the agent to substitute
75
- a shell variable into the table names, which cannot expand inside a quoted heredoc — so
76
- the agent unquotes it, and then the SEARCH TERMS expand too. Both instances driving this
77
- skill did exactly that, unprompted, on the first attempt. Measured consequences, from
78
- `system.query_log`:
79
-
80
- - a term containing `$home` became the empty string, so `LIKE '%%'` matched **every row**
81
- and reported hits with exit 0 — silently wrong, not an error;
82
- - a term containing a backtick **executed the command inside it**.
83
-
84
- A user's search term is arbitrary text. With the heredoc quoted, neither can happen.
85
-
86
- **There is no `sessions_v` object.** The session rollup is a saved query over those same
87
- rooms — `memhouse sessions-query` prints it for whoever you are connected as, ready to
88
- paste into a `FROM (...) AS c` position.
89
-
90
- **The rollup is self-contained.** As of 0.4.5 the printed text carries its own `FINAL` on
91
- both rooms and a trailing `SETTINGS join_use_nulls = 1`, so it is correct wherever you
92
- paste it. Both matter: without `FINAL` every message is counted once per undeleted
93
- ReplacingMergeTree version (2x right after a ship, 3x a few ships later — measured, and it
94
- grows); without `join_use_nulls` a session with no messages reports `total_msgs = 1`
95
- rather than 0.
96
-
97
- **If the `memhouse` binary is not on PATH**, you cannot print the rollup — the skills are
98
- installable on their own. Query the rooms directly instead: `sessions` for metadata
99
- and `messages` for counts, each read `FINAL`, joined on `session_id` and `user_id`.
100
- Prefer the binary when it is there; a rollup you assemble by hand and one printed by a
101
- DIFFERENT memhouse version are the two ways this goes quietly wrong.
102
-
103
-
104
- ## Default listing
105
-
106
- ```sql
107
- SELECT session_id, name, source, host, project,
108
- started, ended, total_msgs, user_msgs, assistant_msgs,
109
- output_tokens,
110
- substring(first_prompt, 1, 80) AS first_prompt
111
- FROM (the rollup from `memhouse sessions-query`)
112
- ORDER BY ended DESC
113
- LIMIT 20
114
- FORMAT PrettyCompact
115
- ```
116
-
117
- - Default `LIMIT 20`; `[N]` overrides.
118
- - `today` / `week` / `month` → `WHERE ended > now() - INTERVAL 1 DAY`
119
- (`7 DAY` / `30 DAY`).
120
- - `project <name>` → `WHERE project ILIKE '%<name>%'`.
121
- - `from <editor>` → `WHERE source = '<id>'` (`claude-code`, `codex`, `cursor`,
122
- `vscode`, `zed`, `opencode`, `gemini-cli`, `windsurf`, …).
123
- - Unnamed sessions (`name = ''`): fall back to `first_prompt` as the label.
124
-
125
- ## More cuts
126
-
127
- Per-editor rollup ("where has my work been happening?"):
128
-
129
- ```sql
130
- SELECT source, count() AS sessions, sum(total_msgs) AS msgs,
131
- sum(output_tokens) AS out_tokens, max(ended) AS last_activity
132
- FROM (the rollup from `memhouse sessions-query`)
133
- GROUP BY source
134
- ORDER BY sessions DESC
135
- FORMAT PrettyCompact
136
- ```
137
-
138
- Busiest projects this month:
139
-
140
- ```sql
141
- SELECT project, count() AS sessions, sum(assistant_msgs) AS assistant_msgs
142
- FROM (the rollup from `memhouse sessions-query`)
143
- WHERE ended > now() - INTERVAL 30 DAY AND project != ''
144
- GROUP BY project
145
- ORDER BY sessions DESC
146
- LIMIT 15
147
- FORMAT PrettyCompact
148
- ```
149
-
150
- ## Examples
151
-
152
- | User says | What you do |
153
- |---|---|
154
- | "what was I working on yesterday?" | `WHERE ended > now() - INTERVAL 1 DAY` |
155
- | "show my last 10 sessions in agentlytics" | `LIMIT 10 … WHERE project ILIKE '%agentlytics%'` |
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 /mem:search, or filter `host` here |
158
-
159
- ## Output
160
-
161
- A compact table: when, name (or first prompt), source, host, project, msgs,
162
- tokens. Offer to pull a full transcript next
163
- (`SELECT role, text FROM messages WHERE session_id = '…' ORDER BY seq`) or to
164
- run /mem:search for a specific quote.
@@ -1,130 +0,0 @@
1
- ---
2
- name: share
3
- description: Share your conversation memory with another user on the same ClickHouse — grant them read-only access to your house, list who can currently read it, or revoke a share. Use when the user says "share my memory with X", "let X see my sessions", "who can read my memory", "stop sharing with X". Read-only by construction — a share can never let anyone write to or delete your memory.
4
- user-invocable: true
5
- argument-hint: "[<user> | revoke <user> | (nothing: list current shares)]"
6
- allowed-tools: Bash
7
- ---
8
-
9
- # /mem:share — a read-only window into your memory
10
-
11
- Three forms, decided by the arguments:
12
-
13
- - `/mem:share yigit` — let `yigit` read this house
14
- - `/mem:share revoke yigit` — close that window
15
- - `/mem:share` — who can read this house right now
16
-
17
- Sharing is a ClickHouse GRANT and nothing else — memhouse adds no layer on top, so what
18
- you grant is exactly what they get: `SELECT`, never write, never delete. Your own grant
19
- carries `WITH GRANT OPTION` on SELECT only (installs from 0.10.0 on), so you can open
20
- this window yourself and can hand on nothing more.
21
-
22
- ## Connection
23
-
24
- **Never print this file or the variables in it.** No `cat "$MH_ENV"`, no `env | grep
25
- MEMHOUSE`, no `set -x` around these commands.
26
-
27
- ```bash
28
- MH_ENV="${MEMHOUSE_HOME:-$HOME/.memhouse}/env"
29
- _u=${MEMHOUSE_URL-}; _s=${MEMHOUSE_USER-}; _p=${MEMHOUSE_PASSWORD-}; _d=${MEMHOUSE_DB-}
30
- set -a; [ -f "$MH_ENV" ] && . "$MH_ENV"; set +a
31
- [ -n "$_u" ] && MEMHOUSE_URL=$_u; [ -n "$_s" ] && MEMHOUSE_USER=$_s
32
- [ -n "$_p" ] && MEMHOUSE_PASSWORD=$_p; [ -n "$_d" ] && MEMHOUSE_DB=$_d
33
- if [ -z "${MEMHOUSE_URL:-}" ] || [ -z "${MEMHOUSE_USER:-}" ]; then
34
- echo "no memhouse house configured — nothing in $MH_ENV and no MEMHOUSE_URL/MEMHOUSE_USER set." >&2
35
- echo "Run: memhouse install" >&2
36
- exit 1
37
- fi
38
-
39
- # NO readonly=1 on this skill's grant/revoke calls — GRANT is refused under readonly.
40
- # The list query below still pins it.
41
- curl -sS --fail-with-body --user "$MEMHOUSE_USER:${MEMHOUSE_PASSWORD:-}" \
42
- --data-binary @- "$MEMHOUSE_URL/?database=${MEMHOUSE_DB:-$MEMHOUSE_USER}" <<'SQL'
43
- <the statement>
44
- SQL
45
- ```
46
-
47
- ## Grant — `/mem:share <user>`
48
-
49
- The user name goes into SQL unquoted: **refuse anything not matching
50
- `[A-Za-z][A-Za-z0-9_]*`** rather than quoting it — a name you cannot type bare is a name
51
- that will be gotten wrong everywhere else too.
52
-
53
- **Validate `<user>` FIRST, once, and use only the validated token — in the GRANT, the
54
- REVOKE, and the house_meta record alike.** The record INSERT is a quoted literal;
55
- anything that has not passed the name check must never reach it.
56
-
57
- **`<db>` below is the LIVE value of `$MEMHOUSE_DB` — print it with the connection
58
- (`echo "house: $MEMHOUSE_DB"`) and use THAT in every statement and every piece of
59
- advice.** Driven live, a model filled `<db>` in the admin advice with the literal
60
- fallback `mem` while the real house was named differently — advice the admin would have
61
- run against the wrong database.
62
-
63
- ```sql
64
- GRANT SELECT ON <db>.* TO <user>
65
- ```
66
-
67
- (`<db>` is `$MEMHOUSE_DB`.) Then RECORD it in the house's own memory — a member cannot
68
- read `system.grants` (measured: `Not enough privileges … SELECT ON system.grants`), so
69
- this record is what the list form reads back:
70
-
71
- ```sql
72
- INSERT INTO house_meta (key, value) VALUES ('share:<user>', 'granted <today, YYYY-MM-DD>')
73
- ```
74
-
75
- Verify the grant took by USING it if you can, or simply report the server accepted it —
76
- do not call `SHOW GRANTS FOR <user>` (it needs privileges a member does not hold).
77
-
78
- Two failure shapes, both to report exactly:
79
-
80
- - `There is no user ... in user directories` — the person has no ClickHouse user on this
81
- server yet. Joining comes first: they run `memhouse install` against this server (or
82
- the admin runs `memhouse install --print-sql --member <user> --db <their-house>`).
83
- A share cannot create a user, by design.
84
- - `Not enough privileges` — either this house predates 0.10.0 (your own grant carries
85
- no grant option), or **the configured house is not yours to share** (a team house, or
86
- someone else's). Only suggest the admin remedy when the house is the user's OWN — the
87
- grant-option statement hands them standing power to share it, which is the owner's
88
- decision, not a fix. For their own pre-0.10 house, print for the ADMIN:
89
- `GRANT SELECT ON <db>.* TO <user>` — and to make future shares self-serve:
90
- `GRANT SELECT ON <db>.* TO <your-user> WITH GRANT OPTION`.
91
- For a house that is not theirs, say so and stop: the owner shares it, not you.
92
-
93
- Tell the user how their friend actually reads it: qualified names —
94
- `SELECT … FROM <db>.messages …` — from /mem:ask, /mem:search or /mem:sql on the friend's
95
- side, with the same epoch filter those skills already carry.
96
-
97
- ## Revoke — `/mem:share revoke <user>`
98
-
99
- ```sql
100
- REVOKE SELECT ON <db>.* FROM <user>
101
- ```
102
- ```sql
103
- INSERT INTO house_meta (key, value) VALUES ('share:<user>', 'revoked <today, YYYY-MM-DD>')
104
- ```
105
-
106
- Revoking a share that never existed is a no-op, not an error — say which it was (the
107
- `share:<user>` record answers that).
108
-
109
- ## List — `/mem:share` (no arguments)
110
-
111
- From the house's own record (append `&readonly=1` to the URL for this one):
112
-
113
- ```sql
114
- SELECT substring(key, 7) AS user, value AS state,
115
- formatDateTime(updated_at, '%Y-%m-%d %H:%i') AS at
116
- FROM house_meta FINAL WHERE key LIKE 'share:%' ORDER BY key
117
- ```
118
-
119
- Latest-wins per user: `granted …` rows are live shares, `revoked …` rows are closed
120
- ones. Be honest about the record's edge: it shows shares made THROUGH THIS SKILL — a
121
- grant the admin issued directly will not appear. The authoritative list is the admin's
122
- one query: `SELECT user_name, access_type FROM system.grants WHERE database = '<db>'`
123
- (members cannot run it; `system.grants` needs privileges a member does not hold).
124
-
125
- ## What this never does
126
-
127
- No `GRANT ALL`, no write grants, no `WITH GRANT OPTION` onward, no user creation, no
128
- grants on any database but your own. If the user asks for more than read access, that is
129
- the shared-house model (both members in ONE database) — a decision, not a grant; point
130
- them at `memhouse install --db <shared>` and stop.
@@ -1,139 +0,0 @@
1
- ---
2
- name: status
3
- description: Show the state of the memory system — what the house holds (sessions, messages, tokens per editor), how fresh it is, its schema generation and migration state, and every machine writing into it. Use when the user asks "is my memory working", "how much is stored", "when did it last ship", "what's in the house", "which machines are shipping", or before debugging why a search found nothing.
4
- user-invocable: true
5
- argument-hint: ""
6
- allowed-tools: Bash
7
- ---
8
-
9
- # /mem:status — the state of the memory system
10
-
11
- **Invoking this skill IS the request. Run the report immediately — connect, run the four
12
- sections below, and present the summary. There are no arguments and nothing to clarify;
13
- do not describe this skill or ask what to check.** (Driven live, a model did exactly
14
- that: it summarized the file and asked "is there something specific?" — a status command
15
- that answers with a question has failed.)
16
-
17
- One report, four sections: what the house holds, how fresh it is, what generation it is
18
- at, and who writes into it. Read-only; nothing here changes anything.
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
- ```bash
27
- MH_ENV="${MEMHOUSE_HOME:-$HOME/.memhouse}/env"
28
- _u=${MEMHOUSE_URL-}; _s=${MEMHOUSE_USER-}; _p=${MEMHOUSE_PASSWORD-}; _d=${MEMHOUSE_DB-}
29
- set -a; [ -f "$MH_ENV" ] && . "$MH_ENV"; set +a
30
- [ -n "$_u" ] && MEMHOUSE_URL=$_u; [ -n "$_s" ] && MEMHOUSE_USER=$_s
31
- [ -n "$_p" ] && MEMHOUSE_PASSWORD=$_p; [ -n "$_d" ] && MEMHOUSE_DB=$_d
32
- if [ -z "${MEMHOUSE_URL:-}" ] || [ -z "${MEMHOUSE_USER:-}" ]; then
33
- echo "no memhouse house configured — nothing in $MH_ENV and no MEMHOUSE_URL/MEMHOUSE_USER set." >&2
34
- echo "Run: memhouse install" >&2
35
- exit 1
36
- fi
37
-
38
- curl -sS --fail-with-body --user "$MEMHOUSE_USER:${MEMHOUSE_PASSWORD:-}" \
39
- --data-binary @- "$MEMHOUSE_URL/?database=${MEMHOUSE_DB:-$MEMHOUSE_USER}&final=1&join_use_nulls=1&readonly=1" <<'SQL'
40
- <the query>
41
- FORMAT PrettyCompact
42
- SQL
43
- ```
44
-
45
- ## The report — run these, then summarize
46
-
47
- **1. What the house holds** — per editor, CURRENT parses only (the rooms retain
48
- superseded parses; an unfiltered count over-reports on any compacted session):
49
-
50
- ```sql
51
- SELECT source,
52
- uniqExact(session_id) AS sessions,
53
- count() AS messages,
54
- sum(input_tokens + output_tokens + cache_read_tokens + cache_write_tokens) AS tokens
55
- FROM messages
56
- WHERE origin != 'ship'
57
- OR (session_id, user_id, epoch) IN (
58
- SELECT session_id, user_id, max(epoch) FROM messages
59
- WHERE origin = 'ship' GROUP BY session_id, user_id)
60
- GROUP BY source ORDER BY sessions DESC
61
- ```
62
-
63
- **2. Freshness and coverage** — when the archive starts, ends, and last received:
64
-
65
- ```sql
66
- SELECT formatDateTime(min(ts), '%Y-%m-%d') AS oldest,
67
- formatDateTime(max(ts), '%Y-%m-%d %H:%i') AS newest,
68
- formatDateTime(max(ingested_at), '%Y-%m-%d %H:%i') AS last_ingest
69
- FROM messages
70
- ```
71
-
72
- A `last_ingest` hours old with the user actively working means the shipper is not
73
- running — say so and point at `memhouse status` / `memhouse start` on that machine.
74
-
75
- **3. Generation and migration state** — the house's record of itself (absent on a
76
- pre-0.10 house; that absence is itself the answer: say the house predates the record):
77
-
78
- ```sql
79
- SELECT key, value FROM house_meta FINAL
80
- WHERE key IN ('schema_version', 'min_writer_schema') ORDER BY key
81
- ```
82
- ```sql
83
- SELECT id, argMax(status, event_at) AS status,
84
- formatDateTime(max(event_at), '%Y-%m-%d %H:%i') AS at
85
- FROM house_events WHERE kind = 'migration' GROUP BY id ORDER BY at DESC LIMIT 3
86
- ```
87
-
88
- A `pending` here with nothing running means an interrupted migration — name
89
- `memhouse migrate` (it refuses stale leftovers legibly rather than clobbering them).
90
-
91
- **4. The fleet** — every machine writing into this house, from the DATA (a pre-0.10
92
- writer records nothing about itself, so rows-without-record is the finding):
93
-
94
- ```sql
95
- SELECT user_id, host, count() AS rows,
96
- formatDateTime(max(ingested_at), '%Y-%m-%d %H:%i') AS last_write
97
- FROM messages GROUP BY user_id, host ORDER BY last_write DESC
98
- ```
99
- ```sql
100
- SELECT key, value FROM house_meta FINAL
101
- WHERE key LIKE 'client_version:%' OR key LIKE 'client_schema:%' OR key LIKE 'last_ship:%'
102
- ```
103
-
104
- Match `client_*:<member>@<host>` against the data rows. A `(user_id, host)` pair with
105
- rows but NO `client_*` record is a **pre-0.10 memhouse still writing** — flag it: its
106
- re-ships delete retained parses, and the fixes are upgrade it or (as admin)
107
- `REVOKE ALTER DELETE, ALTER UPDATE ON <db>.* FROM <member>`.
108
-
109
- **5. Client freshness** — is the installed memhouse behind the published release? This is a
110
- shell check, not SQL (it reads the local binary and the npm registry, no house involved):
111
-
112
- ```bash
113
- INSTALLED=$(memhouse version 2>/dev/null | head -1 | tr -d '[:space:]')
114
- LATEST=$(curl -sS -m 5 https://registry.npmjs.org/memhouse/latest 2>/dev/null | sed -n 's/.*"version":"\([^"]*\)".*/\1/p')
115
- echo "installed=${INSTALLED:-unknown} latest=${LATEST:-unknown}"
116
- ```
117
-
118
- Compare the two as `MAJOR.MINOR.PATCH`. Report freshness, and size the nudge to the gap:
119
-
120
- - **up to date** (installed ≥ latest) — one line: "client 0.X.Y, current."
121
- - **small gap** (same major+minor, a patch or two behind) — a gentle note: "a newer patch
122
- 0.X.Z is out; `memhouse update` when convenient."
123
- - **big gap** (latest MAJOR or MINOR is ahead, or many patches) — surface it prominently and
124
- **offer to update**: "your memhouse is 0.A.B but 0.C.D is released — run **`memhouse
125
- update`** to upgrade; it also refreshes the `/mem:*` plugin, then `/reload-plugins`."
126
- - either side **unknown** (no binary on PATH, or the registry did not answer) — say which,
127
- don't guess. A plugin-only install with no CLI can't self-update; point at
128
- `npm i -g memhouse`.
129
-
130
- Never run `memhouse update` yourself — it restarts daemons and may migrate the house.
131
- Offer the command; the pilot runs it.
132
-
133
- ## Presenting it
134
-
135
- Lead with one line of verdict ("healthy: N sessions across M editors, last ingest X
136
- minutes ago" — or the one thing wrong). Then the per-editor table, then anything from
137
- sections 3–5 that needs action. A **big version gap (section 5) is an action** — put the
138
- `memhouse update` offer near the top, not buried. Skip empty sections; do not pad. If the
139
- house is unreachable, say which URL refused and stop — never guess another endpoint.