memhouse 0.8.0 → 0.11.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 +84 -47
- package/bin/memhouse.js +993 -89
- package/editors/antigravity.js +7 -15
- package/editors/cursor.js +18 -24
- package/editors/goose.js +9 -21
- package/editors/index.js +19 -70
- package/editors/opencode.js +6 -15
- package/editors/sqlite.js +74 -0
- package/editors/windsurf.js +3 -3
- package/editors/zed.js +10 -15
- package/memhouse/COMPETITION.md +9 -5
- package/memhouse/DESIGN.md +37 -12
- package/memhouse/RESUME-AND-UPDATE.md +7 -10
- package/memhouse/delivery/AGENT-INSTALL-PROMPT.md +10 -2
- package/memhouse/delivery/AGENT-INSTALL.md +56 -32
- package/memhouse/delivery/PROMPT.md +37 -13
- package/memhouse/delivery/kernel-install.md +4 -4
- package/memhouse/delivery/plugin/.claude-plugin/plugin.json +14 -5
- package/memhouse/delivery/plugin/skills/ask/SKILL.md +112 -0
- package/memhouse/delivery/plugin/skills/hello/SKILL.md +76 -0
- package/memhouse/delivery/plugin/skills/invite/SKILL.md +65 -0
- package/memhouse/delivery/plugin/skills/search/SKILL.md +1 -1
- package/memhouse/delivery/plugin/skills/sessions/SKILL.md +3 -3
- package/memhouse/delivery/plugin/skills/share/SKILL.md +130 -0
- package/memhouse/delivery/plugin/skills/sql/SKILL.md +34 -11
- package/memhouse/delivery/plugin/skills/status/SKILL.md +114 -0
- package/memhouse/delivery/plugin/skills/users/SKILL.md +97 -0
- package/memhouse/house/HOUSE.md +40 -5
- package/memhouse/house/house.js +191 -10
- package/memhouse/house/migrate.js +277 -0
- package/memhouse/house/migrations/0100-epoch-key.js +68 -0
- package/memhouse/house/schema.sql.tpl +96 -25
- package/memhouse/self-update.js +4 -4
- package/memhouse/service.js +7 -2
- package/memhouse/shipper/ship.js +471 -131
- package/package.json +3 -6
|
@@ -95,18 +95,15 @@ original to duplicate the copy.
|
|
|
95
95
|
|
|
96
96
|
### The gap
|
|
97
97
|
|
|
98
|
-
`npm i -g memhouse@latest` does
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
1.
|
|
102
|
-
|
|
103
|
-
read zero sessions on every pass afterwards.
|
|
104
|
-
2. **The daemons keep running the old code.** See §3.
|
|
105
|
-
3. **The house can be older than the shipper.** A newer shipper may need a column an
|
|
98
|
+
`npm i -g memhouse@latest` does half the job, and the other half has cost a real machine
|
|
99
|
+
real sessions:
|
|
100
|
+
|
|
101
|
+
1. **The daemons keep running the old code.** See §3.
|
|
102
|
+
2. **The house can be older than the shipper.** A newer shipper may need a column an
|
|
106
103
|
existing room lacks; it detects that and prints the rebuild, but only if something runs
|
|
107
104
|
it.
|
|
108
105
|
|
|
109
|
-
And from a checkout there is a
|
|
106
|
+
And from a checkout there is a third: `git pull` updates `ui/src` and leaves the built
|
|
110
107
|
bundle in `public/`, so the dashboard serves the previous release however often it is
|
|
111
108
|
restarted. A published tarball is immune — `prepack` builds `public/` before publish.
|
|
112
109
|
|
|
@@ -114,7 +111,7 @@ restarted. A published tarball is immune — `prepack` builds `public/` before p
|
|
|
114
111
|
|
|
115
112
|
| Kind | Detected by | Action |
|
|
116
113
|
|---|---|---|
|
|
117
|
-
| `global` | under `npm prefix -g` | `npm i -g memhouse@latest
|
|
114
|
+
| `global` | under `npm prefix -g` | `npm i -g memhouse@latest` |
|
|
118
115
|
| `checkout` | a `.git` at the root | `git pull --ff-only` → `npm install` → `npm run build` |
|
|
119
116
|
| `local-dep` | `node_modules/memhouse`, not under the global prefix | refuse; print the right command for where it lives |
|
|
120
117
|
| `npx` | `_npx` in the resolved path | refuse — the next `npx` resolves the registry anyway |
|
|
@@ -42,8 +42,14 @@ already exist.
|
|
|
42
42
|
echo a password into the transcript you are writing, which memhouse will then ship.
|
|
43
43
|
- If a command fails, read what it printed before retrying. memhouse's failures name the
|
|
44
44
|
cause and usually name the fix — it is not a generic tool that fails generically.
|
|
45
|
-
-
|
|
46
|
-
`
|
|
45
|
+
- The house's rooms are three PLAIN, SHARED tables — `sessions`, `messages`,
|
|
46
|
+
`tool_calls` — in the connection's database. No suffixes, no per-member tables.
|
|
47
|
+
Direct reads of `messages`/`tool_calls` must filter to the current parse (the shipper
|
|
48
|
+
retains superseded parses under an `epoch` column); the installed skills carry the
|
|
49
|
+
filter, so prefer them for queries.
|
|
50
|
+
- If `ship` refuses with "wrong sorting key … memhouse migrate", the house was built by
|
|
51
|
+
an older memhouse. Run `memhouse migrate` — it copies, swaps atomically, deletes
|
|
52
|
+
nothing, and shipping resumes on its own. Do NOT try to rebuild tables by hand.
|
|
47
53
|
|
|
48
54
|
## When you are done
|
|
49
55
|
|
|
@@ -52,6 +58,8 @@ mark. Then tell them:
|
|
|
52
58
|
|
|
53
59
|
- where the dashboard is (`http://localhost:{{PORT}}`) and that `memhouse start` runs it
|
|
54
60
|
- that `memhouse search <terms>` searches every past session from every editor
|
|
61
|
+
- that `memhouse plugins install claude` gives their agents /mem:ask, /mem:search,
|
|
62
|
+
/mem:sessions, /mem:share, /mem:sql, /mem:status, /mem:users — offer to run it
|
|
55
63
|
- that you can now search their history yourself, and offer to try one query
|
|
56
64
|
|
|
57
65
|
If `doctor` reports a failure, say which line failed and what memhouse suggested. Do not
|
|
@@ -7,21 +7,21 @@ in order; every command is copy-pasteable.
|
|
|
7
7
|
machine it runs on — which editors have sessions, whether a ClickHouse answers, whether
|
|
8
8
|
docker or podman exists — and states the one route that applies. This file is the static
|
|
9
9
|
long-form version and assumes a reachable ClickHouse already exists, which is the case
|
|
10
|
-
`deploy --local` was added to remove. Prereqs: Node >=
|
|
10
|
+
`deploy --local` was added to remove. Prereqs: Node >= 24 and a reachable
|
|
11
11
|
ClickHouse >= 26.2 (local server, Cloud, or a kernel house — see
|
|
12
12
|
`kernel-install.md` for the kernel path). No checkout required — memhouse is on npm.
|
|
13
13
|
|
|
14
14
|
## 1. Install
|
|
15
15
|
|
|
16
16
|
```bash
|
|
17
|
-
npm install -g memhouse
|
|
17
|
+
npm install -g memhouse
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
-
`--allow-scripts=better-sqlite3
|
|
21
|
-
goose, opencode, zed, antigravity)
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
`memhouse discover` names any adapter it had to skip.
|
|
20
|
+
No flags. This line used to require `--allow-scripts=better-sqlite3`, without which
|
|
21
|
+
five adapters (cursor, goose, opencode, zed, antigravity) silently returned zero
|
|
22
|
+
sessions and you shipped a partial history. SQLite now comes from `node:sqlite`, which
|
|
23
|
+
is part of Node, so there is no install script and nothing to allow. Step 4 still
|
|
24
|
+
verifies — `memhouse discover` names any adapter it had to skip, for any reason.
|
|
25
25
|
|
|
26
26
|
On `EACCES`, decide by **who owns the prefix root**. Not the path — Homebrew's
|
|
27
27
|
prefix is `/usr/local` on Intel and `/opt/homebrew` on Apple Silicon and is
|
|
@@ -34,7 +34,7 @@ ls -ld "$(npm prefix -g)" # the prefix ROOT decides
|
|
|
34
34
|
```
|
|
35
35
|
|
|
36
36
|
Prefix root owned by `root` → a system-managed Node; re-run as
|
|
37
|
-
`sudo npm install -g memhouse
|
|
37
|
+
`sudo npm install -g memhouse`.
|
|
38
38
|
|
|
39
39
|
Prefix root owned by the current user → the prefix is theirs (Homebrew, fnm, nvm,
|
|
40
40
|
volta), so the root-owned file npm tripped on is a stray from an earlier `sudo npm`
|
|
@@ -42,31 +42,34 @@ and another `sudo` adds more. Repair that path instead:
|
|
|
42
42
|
`sudo chown -R "$(id -u):$(id -g)" <path npm named>` (same for `~/.npm`).
|
|
43
43
|
|
|
44
44
|
From a checkout instead (contributors): `npm install --no-audit --no-fund` in the
|
|
45
|
-
repo root —
|
|
46
|
-
|
|
45
|
+
repo root — nothing compiles — then substitute `node bin/memhouse.js` for `memhouse`
|
|
46
|
+
below.
|
|
47
47
|
|
|
48
|
-
## 2. Configure the
|
|
48
|
+
## 2 + 3. Configure, create the schema, first ship — one command
|
|
49
49
|
|
|
50
|
-
|
|
51
|
-
|
|
50
|
+
Do NOT hand-write `~/.memhouse/env`. `install` validates the connection, builds the
|
|
51
|
+
rooms, ships, and writes the config LAST — a hand-written file is read as truth by
|
|
52
|
+
every later command even when it is wrong, and an example URL pasted verbatim points
|
|
53
|
+
at somebody's real house.
|
|
54
|
+
|
|
55
|
+
With a credential the user already has on the house:
|
|
52
56
|
|
|
53
57
|
```bash
|
|
54
|
-
|
|
55
|
-
cat > ~/.memhouse/env <<'EOF'
|
|
56
|
-
MEMHOUSE_URL=http://localhost:8123
|
|
57
|
-
MEMHOUSE_USER=memhouse_root
|
|
58
|
-
MEMHOUSE_PASSWORD=<credential>
|
|
59
|
-
MEMHOUSE_DB=mem
|
|
60
|
-
EOF
|
|
61
|
-
chmod 600 ~/.memhouse/env
|
|
58
|
+
memhouse install --url <house-url> --user <member> --password '<credential>' --db <house>
|
|
62
59
|
```
|
|
63
60
|
|
|
64
|
-
|
|
61
|
+
Or, holding an admin credential, mint the member and the house in one go:
|
|
65
62
|
|
|
66
63
|
```bash
|
|
67
|
-
memhouse install --
|
|
64
|
+
memhouse install --url <house-url> \
|
|
65
|
+
--admin-user <admin> --admin-password '<admin-credential>' \
|
|
66
|
+
--db <house> --member <member> --member-password '<credential>'
|
|
68
67
|
```
|
|
69
68
|
|
|
69
|
+
The admin credential is used once and never stored. Ask the user for values; never
|
|
70
|
+
invent them and never default to localhost:8123 — on many machines that is a real house
|
|
71
|
+
belonging to someone else.
|
|
72
|
+
|
|
70
73
|
Expected output shape: `[memhouse] shipped N sessions (0 skipped) → M msg rows, T
|
|
71
74
|
tool rows in Xs` with N in the hundreds on a machine with real agent usage.
|
|
72
75
|
|
|
@@ -77,14 +80,18 @@ memhouse discover # which adapters were read, and which were SKIPPED
|
|
|
77
80
|
memhouse stats # per-source counts now in the house
|
|
78
81
|
```
|
|
79
82
|
|
|
80
|
-
Read `discover` first. It prints one line per editor it found sessions for, and
|
|
81
|
-
|
|
82
|
-
that
|
|
83
|
+
Read `discover` first. It prints one line per editor it found sessions for, and warns
|
|
84
|
+
by name about any adapter it had to skip. On a healthy install that list is empty: the
|
|
85
|
+
one cause that could take five adapters out at once — a missing native SQLite binding —
|
|
86
|
+
no longer exists, so anything named here is a single editor's own store (locked by a
|
|
87
|
+
running editor, corrupt, or on a schema this parser does not know).
|
|
83
88
|
|
|
84
89
|
`stats` should show one row per source (claude-code, codex, gemini-cli, …) with
|
|
85
|
-
non-zero sessions/messages. Or verify by SQL — rooms are
|
|
86
|
-
|
|
87
|
-
`memhouse sessions-query` prints it
|
|
90
|
+
non-zero sessions/messages. Or verify by SQL — the rooms are plain shared tables
|
|
91
|
+
(`sessions`, `messages`, `tool_calls`) and the session rollup is a saved query rather
|
|
92
|
+
than an object, so `memhouse sessions-query` prints it (it also applies the
|
|
93
|
+
current-parse filter that any hand-written read of `messages` needs — the shipper
|
|
94
|
+
retains superseded parses under an `epoch`):
|
|
88
95
|
|
|
89
96
|
```bash
|
|
90
97
|
curl -s -u "$MEMHOUSE_USER:$MEMHOUSE_PASSWORD" "$MEMHOUSE_URL/?database=$MEMHOUSE_DB" \
|
|
@@ -107,9 +114,26 @@ re-ship loop, so this replaces any separate cron. `memhouse stop` ends both.
|
|
|
107
114
|
memhouse plugins install claude
|
|
108
115
|
```
|
|
109
116
|
|
|
110
|
-
Installs the memhouse plugin into `<claude-config>/skills/
|
|
111
|
-
session as `
|
|
112
|
-
and `/
|
|
117
|
+
Installs the memhouse plugin into `<claude-config>/skills/mem`, which loads next
|
|
118
|
+
session as `mem@skills-dir` and exposes `/mem:ask`, `/mem:search`, `/mem:sessions`,
|
|
119
|
+
`/mem:share`, `/mem:sql`, `/mem:status` and `/mem:users`. They read the connection from
|
|
120
|
+
`$MEMHOUSE_HOME/env` (default
|
|
113
121
|
`~/.memhouse/env`), with exported `MEMHOUSE_*` vars taking precedence, and refuse to run
|
|
114
122
|
rather than guessing a URL when neither is set. `memhouse plugins list` shows what is
|
|
115
123
|
installed, `memhouse plugins remove claude` undoes it.
|
|
124
|
+
|
|
125
|
+
## 7. Upgrading a machine that already ran memhouse < 0.10
|
|
126
|
+
|
|
127
|
+
The 0.10 schema changed the transcript tables' sorting keys, so the first `ship` against
|
|
128
|
+
an old house refuses and names the fix. Run it:
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
memhouse migrate # copies each room, swaps atomically, keeps <room>_pre_epoch
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Nothing is deleted; a service-managed shipper resumes on its next pass by itself.
|
|
135
|
+
`memhouse update` performs this for the user interactively (`--migrate` runs it
|
|
136
|
+
unasked); as an agent, prefer `memhouse update --no-install --migrate` after an
|
|
137
|
+
`npm install -g` you already performed. If OTHER machines still ship into the same
|
|
138
|
+
house with an older memhouse, tell the user to upgrade them promptly — the migration
|
|
139
|
+
output explains why and prints the admin-side alternative.
|
|
@@ -9,9 +9,10 @@ long-term conversation memory.
|
|
|
9
9
|
|
|
10
10
|
You have persistent memory of past agent sessions — every conversation this user
|
|
11
11
|
has had with coding agents (Claude Code, Codex, Gemini CLI, Cursor, and other
|
|
12
|
-
editors), across all of
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
editors), across all of THEIR machines, stored in a ClickHouse database called
|
|
13
|
+
**{{DB}}** (their "house"). The house outlives the transcripts on disk: editors
|
|
14
|
+
compact and delete local session files after weeks, and the house is then the only
|
|
15
|
+
copy.
|
|
15
16
|
Connection: read `{{ENV_FILE}}` (`MEMHOUSE_URL/USER/PASSWORD/DB`); query over
|
|
16
17
|
HTTP with `curl -u "$MEMHOUSE_USER:$MEMHOUSE_PASSWORD" "$MEMHOUSE_URL/?database=$MEMHOUSE_DB"`
|
|
17
18
|
and always add `SETTINGS final=1, join_use_nulls=1` to reads — the second matters
|
|
@@ -22,9 +23,12 @@ work that is not in your current context ("that session where…", "how did I so
|
|
|
22
23
|
X before", "did we ever…", "what was I working on last week"), search memhouse
|
|
23
24
|
FIRST. Only say you don't know after a search comes back empty.
|
|
24
25
|
|
|
25
|
-
**How to query.** Prefer the installed skills when present: `/
|
|
26
|
-
|
|
27
|
-
`/
|
|
26
|
+
**How to query.** Prefer the installed skills when present: `/mem:ask` (answer a
|
|
27
|
+
question from memory, with citations), `/mem:search` (full-text over messages),
|
|
28
|
+
`/mem:sessions` (list/filter sessions), `/mem:sql` (free-form read-only SQL),
|
|
29
|
+
`/mem:status` (what the system holds and its health), `/mem:share` and `/mem:users`
|
|
30
|
+
(read-only sharing between users), `/mem:invite` (mint a new member — inviting comes
|
|
31
|
+
before sharing), `/mem:hello` (introduce the system). Without skills, query directly.
|
|
28
32
|
|
|
29
33
|
**Table names.** The house's rooms are three plain, shared tables — `messages`,
|
|
30
34
|
`sessions`, `tool_calls` — in the connection's database. Everyone in the house writes
|
|
@@ -33,21 +37,41 @@ which machine. Query the whole house by default; add `WHERE user_id = '<name>'`
|
|
|
33
37
|
person. **There is no `sessions_v` object** — the rollup is a saved query, and
|
|
34
38
|
`memhouse sessions-query` prints it ready to drop into a `FROM (...) AS c`.
|
|
35
39
|
|
|
40
|
+
**One session can be stored more than once.** The shipper never deletes: when an editor
|
|
41
|
+
compacts or shortens a session, the new parse is written under a higher `epoch` and the
|
|
42
|
+
old one is retained. Every direct read of `messages`/`tool_calls` must filter to the
|
|
43
|
+
current parse, or counts double and a replay interleaves two versions of the session:
|
|
44
|
+
|
|
45
|
+
```sql
|
|
46
|
+
-- call this CUR below
|
|
47
|
+
WHERE origin != 'ship'
|
|
48
|
+
OR (session_id, user_id, epoch) IN (
|
|
49
|
+
SELECT session_id, user_id, max(epoch) FROM messages
|
|
50
|
+
WHERE origin = 'ship' GROUP BY session_id, user_id)
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Drop CUR deliberately only when asked what a session said BEFORE it was compacted —
|
|
54
|
+
the retained epochs are the only place that content still exists.
|
|
55
|
+
|
|
36
56
|
- Find sessions about a topic (FTS, lowercase your terms):
|
|
37
57
|
`SELECT DISTINCT session_id, any(project), min(ts) FROM messages
|
|
38
|
-
WHERE hasToken(text_word, 'clickhouse') GROUP BY session_id
|
|
58
|
+
WHERE hasToken(text_word, 'clickhouse') AND <CUR> GROUP BY session_id
|
|
39
59
|
ORDER BY 3 DESC LIMIT 10 SETTINGS final=1, join_use_nulls=1 FORMAT PrettyCompact`
|
|
40
60
|
- Recent sessions: `SELECT session_id, source, project, started, first_prompt
|
|
41
61
|
FROM $(memhouse sessions-query) AS c ORDER BY started DESC LIMIT 20 SETTINGS final=1, join_use_nulls=1`
|
|
62
|
+
(the rollup applies the current-parse filter itself)
|
|
42
63
|
- Replay one session: `SELECT role, text FROM messages
|
|
43
|
-
WHERE session_id = '<id>' ORDER BY seq SETTINGS final=1, join_use_nulls=1`
|
|
64
|
+
WHERE session_id = '<id>' AND <CUR> ORDER BY seq SETTINGS final=1, join_use_nulls=1`
|
|
44
65
|
|
|
45
66
|
**Rules.**
|
|
46
|
-
- Memory is READ-ONLY for you
|
|
47
|
-
memhouse shipper alone.
|
|
67
|
+
- Memory is READ-ONLY for you: never INSERT/ALTER/DROP the ROOMS (sessions, messages,
|
|
68
|
+
tool_calls) — ingestion belongs to the memhouse shipper alone. The one exception is
|
|
69
|
+
membership administration through the `memhouse` CLI: `/mem:invite` mints a user and
|
|
70
|
+
`/mem:share` grants read access, both via the tool, not by you writing to the rooms.
|
|
48
71
|
- Quote retrieved content as *the user's past sessions*, and cite the session_id
|
|
49
72
|
when the user may want to dig deeper.
|
|
50
|
-
- Other
|
|
51
|
-
|
|
52
|
-
|
|
73
|
+
- Other users' HOUSES (other databases on the same server) are readable only when
|
|
74
|
+
shared with this credential (`/mem:share` on their side). Query a shared house by
|
|
75
|
+
qualified names — `polat.messages` — with the same CUR filter. An empty result
|
|
76
|
+
means "nothing visible", not "nothing ever happened".
|
|
53
77
|
- Do not paste credentials from `{{ENV_FILE}}` into responses, commits, or logs.
|
|
@@ -35,7 +35,7 @@ rotate on first connect (`issue-credential{user:'memhouse_root'}`).
|
|
|
35
35
|
## 2. Owner sets up the house
|
|
36
36
|
|
|
37
37
|
```bash
|
|
38
|
-
npm install -g memhouse
|
|
38
|
+
npm install -g memhouse
|
|
39
39
|
memhouse install --yes \
|
|
40
40
|
--url https://<kernel-host>:8443 \
|
|
41
41
|
--user memhouse_root \
|
|
@@ -46,9 +46,9 @@ memhouse install --yes \
|
|
|
46
46
|
That writes `~/.memhouse/env`, applies the schema, and runs the first ship.
|
|
47
47
|
`memhouse start` then keeps it fresh on a 300s loop.
|
|
48
48
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
49
|
+
No flags: SQLite comes from `node:sqlite`, so there is no native binding to build
|
|
50
|
+
and no adapter that can go dark for want of one. `memhouse discover` still names any
|
|
51
|
+
adapter it had to skip — now always a single editor's own store.
|
|
52
52
|
|
|
53
53
|
The kernel never runs or reads any of this — it is content-blind; the shipper is
|
|
54
54
|
the agency's own resident, a deterministic `worker` on a loop (no LLM, not an
|
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
{
|
|
2
|
-
"name": "
|
|
3
|
-
"description": "Conversation memory for AI agents over the memhouse typed ClickHouse store: search, browse,
|
|
4
|
-
"version": "0.
|
|
5
|
-
"author": {
|
|
2
|
+
"name": "mem",
|
|
3
|
+
"description": "Conversation memory for AI agents over the memhouse typed ClickHouse store: ask, search, browse, analyze, check, share, and invite across all 17 supported editors and machines. Skills install as /mem:hello, /mem:ask, /mem:search, /mem:sessions, /mem:share, /mem:invite, /mem:sql, /mem:status, and /mem:users.",
|
|
4
|
+
"version": "0.11.0",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "Ramazan Polat"
|
|
7
|
+
},
|
|
6
8
|
"homepage": "https://github.com/agent-realm/memhouse",
|
|
7
|
-
"keywords": [
|
|
9
|
+
"keywords": [
|
|
10
|
+
"memory",
|
|
11
|
+
"clickhouse",
|
|
12
|
+
"search",
|
|
13
|
+
"sessions",
|
|
14
|
+
"analytics",
|
|
15
|
+
"agentlytics"
|
|
16
|
+
]
|
|
8
17
|
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ask
|
|
3
|
+
description: Answer a question FROM conversation memory — retrieve the relevant past sessions out of the memhouse ClickHouse store, read the actual transcripts, and synthesize an answer with citations. Use when the user asks something their past work already answered, e.g. "how did I fix X last time", "what approach did we settle on for Y", "why did we choose Z", "have I solved this error before", "what did the reviewer say about W". This is the retrieve-and-answer skill; /mem:search is find-the-session, /mem:sql is run-a-query.
|
|
4
|
+
user-invocable: true
|
|
5
|
+
argument-hint: "<question about past work>"
|
|
6
|
+
allowed-tools: Bash
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# /mem:ask — answer from conversation memory
|
|
10
|
+
|
|
11
|
+
Answer the user's question from what the house holds, not from what you assume. The
|
|
12
|
+
house is the archive that OUTLIVES the transcripts on disk — Claude Code compacts and
|
|
13
|
+
deletes local session files, so for anything older than ~30 days memhouse is the only
|
|
14
|
+
place the answer still exists.
|
|
15
|
+
|
|
16
|
+
The synthesis is yours; the store is not. memhouse itself runs no LLM anywhere in its
|
|
17
|
+
write or read path — this skill retrieves rows and YOU read them. Never present a guess
|
|
18
|
+
as a retrieval: if the house has nothing on the question, say so.
|
|
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
|
+
Credentials resolve as **flags > exported `MEMHOUSE_*` > `$MEMHOUSE_HOME/env`**
|
|
27
|
+
(default `~/.memhouse/env`) — the same order the `memhouse` CLI uses:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
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
|
+
if [ -z "${MEMHOUSE_URL:-}" ] || [ -z "${MEMHOUSE_USER:-}" ]; then
|
|
36
|
+
echo "no memhouse house configured — nothing in $MH_ENV and no MEMHOUSE_URL/MEMHOUSE_USER set." >&2
|
|
37
|
+
echo "Run: memhouse install" >&2
|
|
38
|
+
exit 1
|
|
39
|
+
fi
|
|
40
|
+
|
|
41
|
+
curl -sS --fail-with-body --user "$MEMHOUSE_USER:${MEMHOUSE_PASSWORD:-}" \
|
|
42
|
+
--data-binary @- "$MEMHOUSE_URL/?database=${MEMHOUSE_DB:-mem}&final=1&join_use_nulls=1&readonly=1" <<'SQL'
|
|
43
|
+
<the query>
|
|
44
|
+
FORMAT JSONEachRow
|
|
45
|
+
SQL
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
`readonly=1` is pinned on every request: this skill never writes.
|
|
49
|
+
|
|
50
|
+
## The method — retrieve, read, answer, cite
|
|
51
|
+
|
|
52
|
+
**Escaping, before anything touches SQL.** Search terms, session ids and user ids are
|
|
53
|
+
arbitrary text going inside single-quoted literals: escape backslashes then single
|
|
54
|
+
quotes (`\` → `\\`, `'` → `\'`) in every value you splice in. A term like `O'Reilly`
|
|
55
|
+
otherwise breaks the query — and crafted text could reshape it.
|
|
56
|
+
|
|
57
|
+
**1. Extract search terms from the question.** Not the question verbatim — the terms
|
|
58
|
+
that would appear in the ANSWER. "How did I fix the ClickHouse auth error?" → search
|
|
59
|
+
`ClickHouse auth`, `Authentication failed`, `ACCESS_DENIED` — error strings beat
|
|
60
|
+
paraphrases, identifiers beat prose. Run 2–3 term variants; the FTS columns are
|
|
61
|
+
lowercased, so lowercase your needle.
|
|
62
|
+
|
|
63
|
+
```sql
|
|
64
|
+
SELECT session_id, any(folder) AS folder,
|
|
65
|
+
formatDateTime(max(ts), '%Y-%m-%d') AS day, count() AS hits,
|
|
66
|
+
substring(anyIf(text, positionCaseInsensitive(text, 'NEEDLE') > 0), 1, 300) AS sample
|
|
67
|
+
FROM messages
|
|
68
|
+
WHERE text_ngram LIKE '%needle%'
|
|
69
|
+
GROUP BY session_id ORDER BY max(ts) DESC LIMIT 8
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
**2. Read the strongest sessions — the actual turns, not just the hit line.** Pull a
|
|
73
|
+
window around the hits so you see the resolution, not the complaint:
|
|
74
|
+
|
|
75
|
+
```sql
|
|
76
|
+
SELECT seq, role, substring(text, 1, 2000) AS text
|
|
77
|
+
FROM messages
|
|
78
|
+
WHERE session_id = '<sid>' AND user_id = '<uid>'
|
|
79
|
+
AND seq BETWEEN <hit_seq - 3> AND <hit_seq + 12>
|
|
80
|
+
ORDER BY seq
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Join on `(session_id, user_id)` always — two housemates can collide on an
|
|
84
|
+
adapter-local session id.
|
|
85
|
+
|
|
86
|
+
**3. Answer, then cite.** Lead with the answer; under it list the sessions it came
|
|
87
|
+
from — `session_id`, date, project — so the user can reopen one (`memhouse resume
|
|
88
|
+
<session_id>` prints the command). Quote the decisive lines verbatim where the exact
|
|
89
|
+
wording matters (error text, a command, a config value).
|
|
90
|
+
|
|
91
|
+
**4. Nothing found is an answer too.** Say what you searched and where the house's
|
|
92
|
+
coverage ends (`SELECT min(ts), max(ts), count() FROM messages`) rather than padding.
|
|
93
|
+
|
|
94
|
+
## What can bite
|
|
95
|
+
|
|
96
|
+
- **Rows can hold more than one PARSE of a session** (`origin`/`epoch` are in the key —
|
|
97
|
+
the house retains superseded parses instead of deleting them). Counts and rollups must
|
|
98
|
+
filter to the current epoch:
|
|
99
|
+
```sql
|
|
100
|
+
WHERE origin != 'ship'
|
|
101
|
+
OR (session_id, user_id, epoch) IN (
|
|
102
|
+
SELECT session_id, user_id, max(epoch) FROM messages
|
|
103
|
+
WHERE origin = 'ship' GROUP BY session_id, user_id)
|
|
104
|
+
```
|
|
105
|
+
For step 2's transcript reads, PREFER the filter as well — an unfiltered read of a
|
|
106
|
+
compacted session interleaves two parses. Drop it deliberately only when the question
|
|
107
|
+
is about what a session said BEFORE it was compacted; the superseded epochs are the
|
|
108
|
+
one place that content still exists.
|
|
109
|
+
- **`text` is truncated at 50,000 chars** and tool args at 20,000 — a missing tail is
|
|
110
|
+
truncation, not absence.
|
|
111
|
+
- The rollup (`sessions_v`) is a saved query, not a table — get it with
|
|
112
|
+
`memhouse sessions-query` if you need per-session totals.
|
|
@@ -0,0 +1,76 @@
|
|
|
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:-mem}&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
|
+
## Then introduce it — three states, three openings
|
|
38
|
+
|
|
39
|
+
**Configured and answering** — open with the numbers: "Your memory holds N sessions
|
|
40
|
+
(M messages) going back to <date> — every conversation you've had with coding agents
|
|
41
|
+
(Claude Code, Codex, Cursor, Gemini CLI and 13 more), across all your machines. It
|
|
42
|
+
outlives the transcripts on disk: editors delete their session files after weeks; this
|
|
43
|
+
doesn't." Then the tour below.
|
|
44
|
+
|
|
45
|
+
**Not configured** — say what memhouse IS (one paragraph: a personal, permanent archive
|
|
46
|
+
of every AI coding session, stored in a ClickHouse "house" you own), then how to start:
|
|
47
|
+
`memhouse onboard` (wizard) or `memhouse install --env <file>` if someone invited them
|
|
48
|
+
(the file came from `/mem:invite` on the inviter's side).
|
|
49
|
+
|
|
50
|
+
**Configured but unreachable** — say which URL refused, point at `memhouse doctor`,
|
|
51
|
+
stop. Never guess another endpoint.
|
|
52
|
+
|
|
53
|
+
## The tour (keep it to one screen)
|
|
54
|
+
|
|
55
|
+
What you can DO from this chat:
|
|
56
|
+
|
|
57
|
+
- `/mem:ask <question>` — answer from your past work, with citations ("how did I fix X
|
|
58
|
+
last time?")
|
|
59
|
+
- `/mem:search <terms>` — find the session ("that conversation about Y")
|
|
60
|
+
- `/mem:sessions` — browse and filter what's stored
|
|
61
|
+
- `/mem:sql <question or SQL>` — ad-hoc analytics (tokens, costs, rankings)
|
|
62
|
+
- `/mem:status` — is it healthy, how much is stored, which machines ship into it
|
|
63
|
+
|
|
64
|
+
And around other people — **inviting comes before sharing**:
|
|
65
|
+
|
|
66
|
+
- `/mem:invite <name>` — mint a friend's account + their own house on your ClickHouse,
|
|
67
|
+
hand them one env file; they install with it and have their own memory
|
|
68
|
+
- `/mem:share <name>` — once they exist, open a read-only window into YOUR memory
|
|
69
|
+
(`/mem:share revoke <name>` closes it); `/mem:users` shows who's around
|
|
70
|
+
|
|
71
|
+
Also worth naming: `memhouse start` serves a dashboard at http://localhost:4640 for
|
|
72
|
+
browsing and analytics outside the chat, and everything here is read-only for agents —
|
|
73
|
+
only the shipper writes.
|
|
74
|
+
|
|
75
|
+
End by offering one concrete action: a search for something they mentioned recently, or
|
|
76
|
+
`/mem:status` if they seem unsure it's working. One offer, not a menu.
|
|
@@ -0,0 +1,65 @@
|
|
|
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. `memhouse invite` uses the caller's
|
|
34
|
+
OWN configured credential when it can manage users, which is the common case for
|
|
35
|
+
whoever set the house up, so no admin is needed:
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
memhouse invite <name> --url <url> [--db <shared-house>]
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
If it prints `invite written: …`, go to step 4 — you are done, and no admin secret
|
|
42
|
+
ever touched this conversation.
|
|
43
|
+
|
|
44
|
+
ONLY if it fails with "your configured credential cannot create users" does
|
|
45
|
+
provisioning need a real admin. Do NOT take the admin password in chat — memhouse
|
|
46
|
+
archives this transcript. Hand the user the line to run THEMSELVES (a leading space
|
|
47
|
+
keeps it out of shell history):
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
memhouse invite <name> --url <url> [--db <shared-house>] --admin-user <admin> --admin-password 'PASTE_IT_HERE'
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
4. When it succeeds it prints `invite written: invite-<name>.env`. Relay to the user:
|
|
54
|
+
- hand `invite-<name>.env` to the invitee over a trusted channel (croc, a password
|
|
55
|
+
manager — not chat); the file IS a password
|
|
56
|
+
- the invitee runs `memhouse install --env invite-<name>.env` — it OFFERS to change
|
|
57
|
+
the password to one only they know (they were granted rotation on their own account),
|
|
58
|
+
then deletes the file; so the password you set stops working once they install
|
|
59
|
+
- once installed, sharing works: `/mem:share <name>` on either side
|
|
60
|
+
|
|
61
|
+
## Never
|
|
62
|
+
|
|
63
|
+
Never create a ClickHouse user by running raw SQL with an admin password you were given
|
|
64
|
+
in chat. Never invent an admin credential. Never suggest granting beyond what `memhouse
|
|
65
|
+
invite` does. If the binary is absent, stop and say so — do not improvise the SQL.
|
|
@@ -6,7 +6,7 @@ argument-hint: "<search terms> [in <project>] [last <N> days] [from <editor>]"
|
|
|
6
6
|
allowed-tools: Bash
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
-
# /
|
|
9
|
+
# /mem:search — search conversation memory
|
|
10
10
|
|
|
11
11
|
Search the full message history in the memhouse house. Every room is named for your
|
|
12
12
|
ClickHouse user — `messages`, `sessions` — see **Room names** below.
|
|
@@ -6,7 +6,7 @@ argument-hint: "[N] [project <name>] [from <editor>] [today|week|month]"
|
|
|
6
6
|
allowed-tools: Bash
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
-
# /
|
|
9
|
+
# /mem:sessions — browse session history
|
|
10
10
|
|
|
11
11
|
List/filter sessions from the session rollup — a saved query over your own rooms, not an
|
|
12
12
|
object; `memhouse sessions-query` prints it (session metadata +
|
|
@@ -154,11 +154,11 @@ FORMAT PrettyCompact
|
|
|
154
154
|
| "what was I working on yesterday?" | `WHERE ended > now() - INTERVAL 1 DAY` |
|
|
155
155
|
| "show my last 10 sessions in agentlytics" | `LIMIT 10 … WHERE project ILIKE '%agentlytics%'` |
|
|
156
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 /
|
|
157
|
+
| "which machine did I do the schema work on?" | search via /mem:search, or filter `host` here |
|
|
158
158
|
|
|
159
159
|
## Output
|
|
160
160
|
|
|
161
161
|
A compact table: when, name (or first prompt), source, host, project, msgs,
|
|
162
162
|
tokens. Offer to pull a full transcript next
|
|
163
163
|
(`SELECT role, text FROM messages WHERE session_id = '…' ORDER BY seq`) or to
|
|
164
|
-
run /
|
|
164
|
+
run /mem:search for a specific quote.
|