memhouse 0.15.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: 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.
@@ -1,97 +0,0 @@
1
- ---
2
- name: users
3
- description: Show the people around your memory — who writes into your house and from which machines, which other houses on this ClickHouse you can read, and who can read yours. Use when the user asks "who can see my memory", "whose memory can I see", "which houses are on this server", "who else ships here", or before/after sharing with /mem:share.
4
- user-invocable: true
5
- argument-hint: ""
6
- allowed-tools: Bash
7
- ---
8
-
9
- # /mem:users — who is around your memory
10
-
11
- **Invoking this skill IS the request. Run the report immediately and present it; there
12
- are no arguments and nothing to clarify.** Read-only throughout.
13
-
14
- Throughout, `<your-db>` is the live `$MEMHOUSE_DB` and `<your-user>` the live
15
- `$MEMHOUSE_USER` — take both from the connection environment, never from the
16
- conversation or from examples.
17
-
18
- Answers three questions, best-effort by design: ClickHouse shows a credential only what
19
- it may see, so on someone else's server some sections legitimately come back thin — say
20
- what could not be seen rather than guessing.
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
- curl -sS --fail-with-body --user "$MEMHOUSE_USER:${MEMHOUSE_PASSWORD:-}" \
40
- --data-binary @- "$MEMHOUSE_URL/?database=${MEMHOUSE_DB:-$MEMHOUSE_USER}&readonly=1" <<'SQL'
41
- <the query>
42
- FORMAT PrettyCompact
43
- SQL
44
- ```
45
-
46
- ## The report
47
-
48
- **1. Who writes into YOUR house** — members × machines, from the data (same ground truth
49
- as /mem:status's fleet):
50
-
51
- ```sql
52
- SELECT user_id AS member, host AS machine, count() AS rows,
53
- formatDateTime(max(ingested_at), '%Y-%m-%d %H:%i') AS last_write
54
- FROM messages GROUP BY user_id, host ORDER BY last_write DESC
55
- ```
56
-
57
- **2. Whose memory YOU can read** — every database your credential reaches is one someone
58
- gave you (or your own):
59
-
60
- ```sql
61
- SHOW DATABASES
62
- ```
63
-
64
- Drop `system`, `information_schema`, `INFORMATION_SCHEMA`, `default` unless they hold
65
- rooms. For each OTHER house listed, it is readable because of a grant — one line each:
66
- `house 'polat' — readable (shared with you); query it as polat.messages`.
67
-
68
- **3. Who can read YOURS** — from the house's own share record (`system.grants` needs
69
- privileges a member does not hold, so the record /mem:share keeps is the readable
70
- source; an admin-issued grant will not appear in it, and the report should say so):
71
-
72
- ```sql
73
- SELECT substring(key, 7) AS user, value AS state
74
- FROM house_meta FINAL WHERE key LIKE 'share:%' ORDER BY key
75
- ```
76
-
77
- `granted …` = a live read window (`/mem:share`); `revoked …` = closed. If the caller IS
78
- privileged, prefer the authoritative form:
79
- `SELECT user_name, groupUniqArray(access_type) FROM system.grants WHERE database = '<your-db>' AND user_name != '<your-user>' GROUP BY user_name`
80
- — try it first, fall back to the record on refusal.
81
-
82
- **4. Every user on the server** — usually admin-only; try it, and if refused say
83
- "(full user list needs admin — showing only what this credential can see)":
84
-
85
- ```sql
86
- SELECT name FROM system.users ORDER BY name
87
- ```
88
-
89
- ## Presenting it
90
-
91
- Four short sections in that order, each with its one-line meaning ("2 machines ship into
92
- your house", "you can read polat's memory", "yigit can read yours — read-only", …).
93
- Empty section three means "no shares made through /mem:share" — and ONLY that. The
94
- record cannot see an admin-issued grant, so never promise "nobody can read your memory"
95
- from an empty ledger; say "no shares recorded through this skill — the authoritative
96
- check is the admin's system.grants query" and print it. Point at `/mem:share <user>` /
97
- `/mem:share revoke <user>` for changes.