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.
- package/README.md +1 -1
- package/bin/memhouse.js +296 -22
- package/memhouse/delivery/AGENT-INSTALL-PROMPT.md +2 -2
- package/memhouse/delivery/AGENT-INSTALL.md +2 -2
- package/memhouse/delivery/PROMPT.md +15 -9
- package/memhouse/delivery/plugin/.claude-plugin/plugin.json +2 -2
- package/memhouse/delivery/plugin/reference/HOUSE.md +154 -0
- package/memhouse/delivery/plugin/skills/access/SKILL.md +148 -0
- package/memhouse/delivery/plugin/skills/admin/SKILL.md +29 -0
- package/memhouse/delivery/plugin/skills/house/SKILL.md +99 -0
- package/memhouse/delivery/plugin/skills/recall/SKILL.md +119 -0
- package/memhouse/delivery/plugin/skills/sql/SKILL.md +73 -194
- package/memhouse/flags.js +77 -0
- package/memhouse/share.js +64 -0
- package/package.json +1 -1
- package/memhouse/delivery/plugin/skills/ask/SKILL.md +0 -131
- package/memhouse/delivery/plugin/skills/hello/SKILL.md +0 -121
- package/memhouse/delivery/plugin/skills/invite/SKILL.md +0 -130
- package/memhouse/delivery/plugin/skills/search/SKILL.md +0 -186
- package/memhouse/delivery/plugin/skills/sessions/SKILL.md +0 -164
- package/memhouse/delivery/plugin/skills/share/SKILL.md +0 -130
- package/memhouse/delivery/plugin/skills/status/SKILL.md +0 -139
- package/memhouse/delivery/plugin/skills/users/SKILL.md +0 -97
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: hello
|
|
3
|
-
description: Introduce memhouse — what it is, what it remembers, and how to use it from here. Use when the user asks "what is memhouse", "what can my memory do", "how do I use this", invokes /mem:hello, or seems new to the memory system. Also the right first answer when another /mem skill fails because nothing is configured yet.
|
|
4
|
-
user-invocable: true
|
|
5
|
-
argument-hint: ""
|
|
6
|
-
allowed-tools: Bash
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
# /mem:hello — welcome to your memory
|
|
10
|
-
|
|
11
|
-
**Invoking this skill IS the request. Give the introduction immediately — grounded in
|
|
12
|
-
this machine's actual state, not hypotheticals. Nothing to clarify first.**
|
|
13
|
-
|
|
14
|
-
## First: find out what state this machine is in
|
|
15
|
-
|
|
16
|
-
RUN this yourself with the Bash tool — do not print it for the user to run, and do not
|
|
17
|
-
show the env values it sources. Read its output, then write the introduction using the
|
|
18
|
-
ACTUAL numbers it returns (interpolate them; never emit the literal "N sessions").
|
|
19
|
-
|
|
20
|
-
```bash
|
|
21
|
-
MH_ENV="${MEMHOUSE_HOME:-$HOME/.memhouse}/env"
|
|
22
|
-
_u=${MEMHOUSE_URL-}; _s=${MEMHOUSE_USER-}; _p=${MEMHOUSE_PASSWORD-}; _d=${MEMHOUSE_DB-}
|
|
23
|
-
set -a; [ -f "$MH_ENV" ] && . "$MH_ENV"; set +a
|
|
24
|
-
[ -n "$_u" ] && MEMHOUSE_URL=$_u; [ -n "$_s" ] && MEMHOUSE_USER=$_s
|
|
25
|
-
[ -n "$_p" ] && MEMHOUSE_PASSWORD=$_p; [ -n "$_d" ] && MEMHOUSE_DB=$_d
|
|
26
|
-
if [ -n "${MEMHOUSE_URL:-}" ] && [ -n "${MEMHOUSE_USER:-}" ]; then
|
|
27
|
-
curl -sS -m 5 --user "$MEMHOUSE_USER:${MEMHOUSE_PASSWORD:-}" \
|
|
28
|
-
--data-binary "SELECT uniqExact(session_id), count(), formatDateTime(min(ts),'%Y-%m-%d') FROM messages" \
|
|
29
|
-
"$MEMHOUSE_URL/?database=${MEMHOUSE_DB:-$MEMHOUSE_USER}&final=1&readonly=1" 2>&1
|
|
30
|
-
else
|
|
31
|
-
echo "NOT_CONFIGURED"
|
|
32
|
-
fi
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
(Do not print the env values, and do not narrate that you are withholding them — just use them.)
|
|
36
|
-
|
|
37
|
-
## What memhouse is — the overview (give this in every state, before the tour)
|
|
38
|
-
|
|
39
|
-
One paragraph, grounded not sold: **memhouse is your personal, permanent archive of every
|
|
40
|
-
AI coding session.** A background shipper reads the session files your editors write —
|
|
41
|
-
Claude Code, Codex, Cursor, VS Code, Zed, OpenCode, Gemini CLI and 10 more, 17 in all —
|
|
42
|
-
and ships them into a ClickHouse database you own, called a **house**. YOU choose that
|
|
43
|
-
ClickHouse: a local one on your laptop, one on your own VM, or any ClickHouse server you
|
|
44
|
-
control — managed or remote. memhouse itself is not a hosted service and phones nothing
|
|
45
|
-
home; your sessions go only to the house you point it at, and it runs no LLM in its write
|
|
46
|
-
or read path — it stores rows, the agent reading them (this chat) does the thinking. It
|
|
47
|
-
outlives the transcripts on disk: editors compact and delete their local session files
|
|
48
|
-
after weeks, so for anything older than ~30 days the house is the only place it still
|
|
49
|
-
exists. One archive spans every editor, every project, every machine you ship from.
|
|
50
|
-
|
|
51
|
-
What it gives you, in one breath: **ask** your own history a question and get a cited
|
|
52
|
-
answer, **search** for the session you half-remember, **query** the raw data for numbers,
|
|
53
|
-
browse a **dashboard**, and **invite** other people onto your ClickHouse so memories can be
|
|
54
|
-
**shared** read-only. The tour below names each.
|
|
55
|
-
|
|
56
|
-
## Then introduce it — three states, three openings
|
|
57
|
-
|
|
58
|
-
**Configured and answering** — open with the numbers: "Your memory holds N sessions
|
|
59
|
-
(M messages) going back to <date> — every conversation you've had with coding agents
|
|
60
|
-
(Claude Code, Codex, Cursor, Gemini CLI and 13 more), across all your machines. It
|
|
61
|
-
outlives the transcripts on disk: editors delete their session files after weeks; this
|
|
62
|
-
doesn't." Then the tour below.
|
|
63
|
-
|
|
64
|
-
**Not configured** — say what memhouse IS (one paragraph: a personal, permanent archive
|
|
65
|
-
of every AI coding session, stored in a ClickHouse "house" you own), then how to start:
|
|
66
|
-
`memhouse onboard` (wizard) or `memhouse install --env <file>` if someone invited them
|
|
67
|
-
(the file came from `/mem:invite` on the inviter's side).
|
|
68
|
-
|
|
69
|
-
**Configured but unreachable** — say which URL refused, point at `memhouse doctor`,
|
|
70
|
-
stop. Never guess another endpoint.
|
|
71
|
-
|
|
72
|
-
## The tour (keep it to one screen)
|
|
73
|
-
|
|
74
|
-
What you can DO from this chat:
|
|
75
|
-
|
|
76
|
-
- `/mem:ask <question>` — answer from your past work, with citations ("how did I fix X
|
|
77
|
-
last time?")
|
|
78
|
-
- `/mem:search <terms>` — find the session ("that conversation about Y")
|
|
79
|
-
- `/mem:sessions` — browse and filter what's stored
|
|
80
|
-
- `/mem:sql <question or SQL>` — ad-hoc analytics (tokens, costs, rankings)
|
|
81
|
-
- `/mem:status` — is it healthy, how much is stored, which machines ship into it
|
|
82
|
-
|
|
83
|
-
The first three overlap — same words work in each — so give the reader the rule that
|
|
84
|
-
separates them: **want a session → search. Want an answer → ask. Want a number → sql.**
|
|
85
|
-
`ask` runs a `search` first, then reads the turns it finds and writes you a cited answer;
|
|
86
|
-
`search` stops at the ranked list of sessions; `sql` counts and aggregates the columns
|
|
87
|
-
rather than hunting text.
|
|
88
|
-
|
|
89
|
-
And around other people — **inviting comes before sharing**:
|
|
90
|
-
|
|
91
|
-
- `/mem:invite <name>` — mint a friend's account + their own house on your ClickHouse,
|
|
92
|
-
hand them one env file; they install with it and have their own memory
|
|
93
|
-
- `/mem:share <name>` — once they exist, open a read-only window into YOUR memory
|
|
94
|
-
(`/mem:share revoke <name>` closes it); `/mem:users` shows who's around
|
|
95
|
-
- **Read a friend's shared memory** — once they've shared theirs with you, name their
|
|
96
|
-
house right in the question: `/mem:ask <question> in <their-house>`, or
|
|
97
|
-
`/mem:search <terms> in <their-house>` / `/mem:sql … on <their-house>`. It reads THEIR
|
|
98
|
-
house read-only, with your own credentials; `/mem:users` lists the houses shared with
|
|
99
|
-
you. (A house is a whole ClickHouse database — sharing grants read on it, not on single
|
|
100
|
-
sessions.)
|
|
101
|
-
|
|
102
|
-
Also worth naming: `memhouse start` serves a dashboard at http://localhost:4640 for
|
|
103
|
-
browsing and analytics outside the chat, and everything here is read-only for agents —
|
|
104
|
-
only the shipper writes.
|
|
105
|
-
|
|
106
|
-
**Freshness check** — quietly compare the installed client to the latest release; mention
|
|
107
|
-
it ONLY if there's a real gap (don't clutter the welcome otherwise):
|
|
108
|
-
|
|
109
|
-
```bash
|
|
110
|
-
INSTALLED=$(memhouse version 2>/dev/null | head -1 | tr -d '[:space:]')
|
|
111
|
-
LATEST=$(curl -sS -m 5 https://registry.npmjs.org/memhouse/latest 2>/dev/null | sed -n 's/.*"version":"\([^"]*\)".*/\1/p')
|
|
112
|
-
echo "installed=${INSTALLED:-unknown} latest=${LATEST:-unknown}"
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
If the installed version is a MINOR or MAJOR behind (or many patches), add one line:
|
|
116
|
-
"heads-up — you're on 0.A.B, 0.C.D is out; `memhouse update` upgrades it and refreshes
|
|
117
|
-
these `/mem:*` skills." A patch or two behind isn't worth mentioning here. Never run the
|
|
118
|
-
update yourself.
|
|
119
|
-
|
|
120
|
-
End by offering one concrete action: a search for something they mentioned recently, or
|
|
121
|
-
`/mem:status` if they seem unsure it's working. One offer, not a menu.
|
|
@@ -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.
|