memhouse 0.16.0 → 0.17.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.
@@ -1,130 +0,0 @@
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, from the directory the file should
34
- land in (step 4). `memhouse invite` uses the caller's OWN configured credential when
35
- it can manage users, which is the common case for whoever set the house up, so no
36
- admin is needed:
37
-
38
- ```
39
- memhouse invite <name> --url <url> [--db <shared-house>]
40
- ```
41
-
42
- If it prints `invite written: …`, go to step 4 — you are done, and no admin secret
43
- ever touched this conversation.
44
-
45
- Otherwise read WHICH refusal came back; they need different answers.
46
-
47
- **"cannot reach `<url>` …"** — nothing to do with privileges. The address is wrong,
48
- or the house is bound to loopback (`memhouse deploy --local` binds to 127.0.0.1 on
49
- purpose, so no LAN address reaches it and no invitee could either). Ask the user for
50
- the address the invitee will actually use; do not retry with a guess.
51
-
52
- **"`<user>` is a MEMBER of this ClickHouse, not an administrator"** — inviting needs
53
- CREATE USER and CREATE DATABASE, which a member deliberately does not hold. Two cases,
54
- and you must ask which one applies rather than assuming:
55
-
56
- - **They run the ClickHouse.** They have the admin credential; memhouse never stores
57
- it. Do NOT take that password in chat — memhouse archives this transcript. Hand them
58
- the line to run THEMSELVES, and leave `--admin-password` OFF so it is prompted for,
59
- keeping it out of both the process list and shell history:
60
-
61
- ```
62
- memhouse invite <name> --url <url> [--db <shared-house>] --admin-user <admin>
63
- ```
64
-
65
- **You cannot run that line yourself.** The prompt needs a TTY and your Bash tool has
66
- none, so it refuses with "no TTY to prompt on" — and the fix is NOT to add
67
- `--admin-password`, which would put the credential in this transcript. The user runs
68
- it in their own terminal; you wait for them to say it is done.
69
-
70
- - **Somebody else runs it.** They cannot invite, and no flag changes that. Print the
71
- statements for whoever administers the server — this contacts nothing and needs no
72
- credential:
73
-
74
- ```
75
- memhouse invite <name> --url <url> [--db <shared-house>] --print-sql
76
- ```
77
-
78
- The output ends with the four `MEMHOUSE_*` lines the invitee needs. Those lines are
79
- a credential: tell the user to send them over a trusted channel, and **never print
80
- them into the conversation yourself**.
81
-
82
- **If it refuses with "house `<db>` already exists and holds N messages", STOP.** That
83
- house is somebody's memory, and inviting into it would hand the invitee someone else's
84
- sessions. Do not pass `--adopt` on your own initiative — report the row count and the
85
- writer count to the user and let them choose: a different handle, a different `--db`,
86
- or `--adopt` if sharing that house is genuinely what they want.
87
-
88
- 4. **Put the file where the user can attach it.** The CLI writes `invite-<name>.env`
89
- into the CURRENT directory, and the user has to send it by hand — so run the command
90
- from their downloads directory (on macOS and most Linux desktops `~/Downloads`), or
91
- move it there afterwards. Never leave it in a git repo: it is a password, and
92
- `invite-*.env` is only gitignored in this repo, not in whatever checkout the user
93
- happened to be standing in.
94
-
95
- Confirm it is there and still mode `600`, and say the full path. **Never print the
96
- file's contents** — the password inside would land in a transcript memhouse ships.
97
-
98
- 5. **Say whether the house was fresh.** Run
99
- `memhouse stats` or query `SELECT count() FROM <db>.messages` and tell the user
100
- plainly: a brand-new house reads 0. This is a second line of defence behind step 3's
101
- refusal, and it is how the user learns the invitee is starting clean.
102
-
103
- 6. **Write the message the user will send.** They are about to hand over a file plus an
104
- explanation, so compose it for them rather than making them write it. Put it in a
105
- fenced block they can copy whole, and cover:
106
- - what memhouse is, in a sentence or two — the invitee may never have heard of it
107
- - that the attached `invite-<name>.env` IS a password: don't forward it, don't paste
108
- it into chat
109
- - `npm install -g memhouse` (Node 24+, no flags, nothing compiles), then
110
- `memhouse install --env invite-<name>.env`
111
- - that install OFFERS to change the password to one only they know, then deletes the
112
- file — so they should say yes, because the inviter knows the current one
113
- - `memhouse onboard` to start shipping, `memhouse status` to check it worked
114
- - that their house is theirs alone; the inviter cannot read it unless they run
115
- `/mem:share <inviter>`
116
-
117
- If the invitee is REPLACING an existing setup (they were shipping under another
118
- credential), add `memhouse ship --full` after the install — a plain incremental pass
119
- skips sessions the new house has no record of, so their history would not follow them.
120
-
121
- Then: once installed, sharing works both ways — `/mem:share <name>`.
122
-
123
- ## Never
124
-
125
- Never create a ClickHouse user by running raw SQL with an admin password you were given
126
- in chat. Never invent an admin credential. Never suggest granting beyond what `memhouse
127
- invite` does. If the binary is absent, stop and say so — do not improvise the SQL.
128
-
129
- Never print the invite file's contents, and never pass `--adopt` unless the user asked
130
- for it after being told what is already in that house.
@@ -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.