memhouse 0.0.1 → 0.3.1
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/LICENSE +22 -0
- package/README.md +72 -3
- package/bin/memhouse.js +559 -0
- package/cache.js +850 -0
- package/ch-ingest.js +329 -0
- package/ch-schema.js +152 -0
- package/clickhouse.js +87 -0
- package/editors/adapter-errors.js +52 -0
- package/editors/antigravity.js +1048 -0
- package/editors/base.js +321 -0
- package/editors/claude.js +386 -0
- package/editors/codebuff.js +338 -0
- package/editors/codex.js +537 -0
- package/editors/commandcode.js +166 -0
- package/editors/copilot-jetbrains.js +207 -0
- package/editors/copilot.js +260 -0
- package/editors/cursor-agent.js +202 -0
- package/editors/cursor.js +498 -0
- package/editors/gemini.js +231 -0
- package/editors/goose.js +386 -0
- package/editors/gsd.js +366 -0
- package/editors/index.js +274 -0
- package/editors/kiro.js +315 -0
- package/editors/opencode.js +432 -0
- package/editors/vscode.js +413 -0
- package/editors/windsurf.js +634 -0
- package/editors/zed.js +229 -0
- package/index.js +424 -2
- package/mcp-server.js +294 -0
- package/mem-house/DESIGN.md +119 -0
- package/mem-house/delivery/AGENT-INSTALL.md +87 -0
- package/mem-house/delivery/PROMPT.md +42 -0
- package/mem-house/delivery/install.sh +181 -0
- package/mem-house/delivery/kernel-install.md +83 -0
- package/mem-house/delivery/plugin/.claude-plugin/plugin.json +8 -0
- package/mem-house/delivery/skills/memhouse-search/SKILL.md +104 -0
- package/mem-house/delivery/skills/memhouse-sessions/SKILL.md +94 -0
- package/mem-house/delivery/skills/memhouse-sql/SKILL.md +115 -0
- package/mem-house/rls.sql +17 -0
- package/mem-house/schema.sql +145 -0
- package/mem-house/server/queries.js +878 -0
- package/mem-house/server/server.js +228 -0
- package/mem-house/shipper/ship.js +461 -0
- package/misc/syntax-check.js +38 -0
- package/package.json +59 -7
- package/pricing.js +88 -0
- package/pricing.json +992 -0
- package/public/assets/index-CCPh7a2Z.js +75 -0
- package/public/assets/index-DV6ONi_F.css +2 -0
- package/public/index.html +16 -0
- package/relay-client.js +306 -0
- package/relay-server.js +577 -0
- package/server.js +860 -0
- package/share-image.js +367 -0
- package/ui/eslint.config.js +29 -0
- package/ui/index.html +15 -0
- package/ui/package-lock.json +4229 -0
- package/ui/package.json +38 -0
- package/ui/src/App.jsx +364 -0
- package/ui/src/components/ActivityHeatmap.jsx +206 -0
- package/ui/src/components/AiAuditCard.jsx +254 -0
- package/ui/src/components/AnimatedLoader.jsx +14 -0
- package/ui/src/components/AnimatedLogo.jsx +96 -0
- package/ui/src/components/ChatSidebar.jsx +226 -0
- package/ui/src/components/DateRangePicker.jsx +104 -0
- package/ui/src/components/EditorDot.jsx +11 -0
- package/ui/src/components/EditorIcon.jsx +117 -0
- package/ui/src/components/KpiCard.jsx +9 -0
- package/ui/src/components/LiveFeed.jsx +128 -0
- package/ui/src/components/LoginScreen.jsx +81 -0
- package/ui/src/components/MessageRenderer.jsx +148 -0
- package/ui/src/components/PageHeader.jsx +11 -0
- package/ui/src/components/SectionTitle.jsx +3 -0
- package/ui/src/components/ShareModal.jsx +305 -0
- package/ui/src/components/TokenTimeline.jsx +258 -0
- package/ui/src/hooks/useLive.jsx +15 -0
- package/ui/src/index.css +83 -0
- package/ui/src/lib/api.js +343 -0
- package/ui/src/lib/constants.js +88 -0
- package/ui/src/lib/theme.jsx +23 -0
- package/ui/src/main.jsx +16 -0
- package/ui/src/pages/Artifacts.jsx +575 -0
- package/ui/src/pages/Compare.jsx +232 -0
- package/ui/src/pages/CostAnalysis.jsx +354 -0
- package/ui/src/pages/Dashboard.jsx +529 -0
- package/ui/src/pages/DeepAnalysis.jsx +495 -0
- package/ui/src/pages/GSD.jsx +726 -0
- package/ui/src/pages/MCPs.jsx +744 -0
- package/ui/src/pages/ProjectDetail.jsx +325 -0
- package/ui/src/pages/Projects.jsx +249 -0
- package/ui/src/pages/RelayDashboard.jsx +590 -0
- package/ui/src/pages/RelayUserDetail.jsx +466 -0
- package/ui/src/pages/Sessions.jsx +535 -0
- package/ui/src/pages/Settings.jsx +241 -0
- package/ui/src/pages/SqlViewer.jsx +370 -0
- package/ui/src/pages/Subscriptions.jsx +411 -0
- package/ui/vite.config.js +18 -0
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# mem-house standalone installer.
|
|
3
|
+
#
|
|
4
|
+
# Checks node >= 20 and a reachable ClickHouse, writes ~/.memhouse/env
|
|
5
|
+
# (MEMHOUSE_* vars), applies the house schema via the shipper
|
|
6
|
+
# (`ship.js --ensure-schema`), runs a first ship, and prints how to start the
|
|
7
|
+
# dashboard and set up continuous shipping (`--loop`).
|
|
8
|
+
#
|
|
9
|
+
# Usage:
|
|
10
|
+
# ./install.sh # interactive (prompts for missing values)
|
|
11
|
+
# ./install.sh --url http://localhost:8123 --user memhouse_root --password 'pw' --db memhouse
|
|
12
|
+
# ./install.sh --yes # non-interactive, defaults / env / existing env file
|
|
13
|
+
#
|
|
14
|
+
# Flags:
|
|
15
|
+
# --url URL ClickHouse HTTP(S) endpoint (default http://localhost:8123)
|
|
16
|
+
# --user USER ClickHouse user (default memhouse_root; 'default' works for a local CH)
|
|
17
|
+
# --password PW credential (default empty)
|
|
18
|
+
# --db DB the house (database) (default memhouse)
|
|
19
|
+
# --port PORT dashboard/API port (default 4640)
|
|
20
|
+
# --yes never prompt; take flags > current env > ~/.memhouse/env > defaults
|
|
21
|
+
# --no-ship stop after writing env + ensure-schema (skip the first ship)
|
|
22
|
+
#
|
|
23
|
+
# Defaults resolve in order: flag > MEMHOUSE_* already in the environment >
|
|
24
|
+
# existing ~/.memhouse/env > built-in default. Runs from any cwd — the repo is
|
|
25
|
+
# resolved from this script's own location (mem-house/delivery/ -> repo root).
|
|
26
|
+
set -euo pipefail
|
|
27
|
+
|
|
28
|
+
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
29
|
+
REPO_ROOT="$(cd "$HERE/../.." && pwd)"
|
|
30
|
+
SHIP="$REPO_ROOT/mem-house/shipper/ship.js"
|
|
31
|
+
SERVER="$REPO_ROOT/mem-house/server/server.js"
|
|
32
|
+
ENV_DIR="$HOME/.memhouse"
|
|
33
|
+
ENV_FILE="$ENV_DIR/env"
|
|
34
|
+
|
|
35
|
+
die() { echo "install.sh: $*" >&2; exit 1; }
|
|
36
|
+
info() { echo "== $*"; }
|
|
37
|
+
|
|
38
|
+
# ---------- 0. defaults: existing env file, then environment ----------------
|
|
39
|
+
if [ -f "$ENV_FILE" ]; then
|
|
40
|
+
# shellcheck disable=SC1090
|
|
41
|
+
set -a; . "$ENV_FILE"; set +a
|
|
42
|
+
fi
|
|
43
|
+
CH_URL="${MEMHOUSE_URL:-http://localhost:8123}"
|
|
44
|
+
CH_USER="${MEMHOUSE_USER:-memhouse_root}"
|
|
45
|
+
CH_PASSWORD="${MEMHOUSE_PASSWORD:-}"
|
|
46
|
+
CH_DB="${MEMHOUSE_DB:-memhouse}"
|
|
47
|
+
DASH_PORT="${MEMHOUSE_PORT:-4640}"
|
|
48
|
+
|
|
49
|
+
ASSUME_YES=0
|
|
50
|
+
NO_SHIP=0
|
|
51
|
+
URL_SET=0; USER_SET=0; PASS_SET=0; DB_SET=0
|
|
52
|
+
|
|
53
|
+
while [ $# -gt 0 ]; do
|
|
54
|
+
case "$1" in
|
|
55
|
+
--url) CH_URL="${2:?--url needs a value}"; URL_SET=1; shift 2;;
|
|
56
|
+
--user) CH_USER="${2:?--user needs a value}"; USER_SET=1; shift 2;;
|
|
57
|
+
--password) CH_PASSWORD="${2:?--password needs a value}"; PASS_SET=1; shift 2;;
|
|
58
|
+
--db) CH_DB="${2:?--db needs a value}"; DB_SET=1; shift 2;;
|
|
59
|
+
--port) DASH_PORT="${2:?--port needs a value}"; shift 2;;
|
|
60
|
+
--yes|-y) ASSUME_YES=1; shift;;
|
|
61
|
+
--no-ship) NO_SHIP=1; shift;;
|
|
62
|
+
-h|--help) sed -n '2,25p' "${BASH_SOURCE[0]}" | sed 's/^# \{0,1\}//'; exit 0;;
|
|
63
|
+
*) die "unknown flag: $1 (try --help)";;
|
|
64
|
+
esac
|
|
65
|
+
done
|
|
66
|
+
|
|
67
|
+
# ---------- 1. node >= 20 ---------------------------------------------------
|
|
68
|
+
command -v node >/dev/null 2>&1 || die "node not found (need node >= 20; https://nodejs.org)"
|
|
69
|
+
NODE_MAJOR="$(node -p 'process.versions.node.split(".")[0]')"
|
|
70
|
+
[ "$NODE_MAJOR" -ge 20 ] 2>/dev/null || die "node >= 20 required (found $(node -v))"
|
|
71
|
+
info "node $(node -v) ok"
|
|
72
|
+
|
|
73
|
+
[ -f "$SHIP" ] || die "shipper not found at $SHIP (incomplete checkout?)"
|
|
74
|
+
|
|
75
|
+
# ---------- 2. repo dependencies -------------------------------------------
|
|
76
|
+
if [ ! -d "$REPO_ROOT/node_modules" ]; then
|
|
77
|
+
info "installing npm dependencies (first run)"
|
|
78
|
+
(cd "$REPO_ROOT" && npm install --no-audit --no-fund)
|
|
79
|
+
fi
|
|
80
|
+
|
|
81
|
+
# ---------- 3. gather connection values -------------------------------------
|
|
82
|
+
prompt() { # prompt <question> <current> -> stdout
|
|
83
|
+
local ans
|
|
84
|
+
read -r -p "$1 [$2]: " ans
|
|
85
|
+
echo "${ans:-$2}"
|
|
86
|
+
}
|
|
87
|
+
if [ "$ASSUME_YES" -eq 0 ] && [ -t 0 ]; then
|
|
88
|
+
[ "$URL_SET" -eq 1 ] || CH_URL="$(prompt "ClickHouse URL" "$CH_URL")"
|
|
89
|
+
[ "$USER_SET" -eq 1 ] || CH_USER="$(prompt "ClickHouse user" "$CH_USER")"
|
|
90
|
+
if [ "$PASS_SET" -eq 0 ]; then
|
|
91
|
+
read -r -s -p "ClickHouse password [keep current/empty]: " ans; echo
|
|
92
|
+
[ -n "$ans" ] && CH_PASSWORD="$ans"
|
|
93
|
+
fi
|
|
94
|
+
[ "$DB_SET" -eq 1 ] || CH_DB="$(prompt "House (database)" "$CH_DB")"
|
|
95
|
+
fi
|
|
96
|
+
|
|
97
|
+
# ---------- 4. ClickHouse reachable? ----------------------------------------
|
|
98
|
+
info "checking ClickHouse at $CH_URL (user $CH_USER)"
|
|
99
|
+
ONE="$(curl -sS --connect-timeout 5 --fail-with-body \
|
|
100
|
+
--user "$CH_USER:$CH_PASSWORD" \
|
|
101
|
+
--data-binary "SELECT 1" "$CH_URL")" \
|
|
102
|
+
|| die "ClickHouse not reachable at $CH_URL with those credentials"
|
|
103
|
+
[ "$ONE" = "1" ] || die "unexpected reply from $CH_URL: $ONE"
|
|
104
|
+
info "ClickHouse ok"
|
|
105
|
+
|
|
106
|
+
# Ensure the house exists before ensure-schema (schema.sql is unqualified and the
|
|
107
|
+
# shipper binds to MEMHOUSE_DB — on a virgin standalone server the database must
|
|
108
|
+
# be created first). On a kernel realm the house is already provisioned; if the
|
|
109
|
+
# user lacks CREATE DATABASE but the house is reachable, that's fine too.
|
|
110
|
+
case "$CH_DB" in
|
|
111
|
+
(*[!A-Za-z0-9_]*|'') die "invalid database name '$CH_DB' — use letters, digits, underscore" ;;
|
|
112
|
+
esac
|
|
113
|
+
if ! curl -fsS --max-time 20 --user "$CH_USER:$CH_PASSWORD" \
|
|
114
|
+
--data-binary "CREATE DATABASE IF NOT EXISTS $CH_DB" "$CH_URL" >/dev/null 2>&1; then
|
|
115
|
+
curl -fsS --max-time 20 --user "$CH_USER:$CH_PASSWORD" \
|
|
116
|
+
--data-binary "SELECT 1" "$CH_URL/?database=$CH_DB" >/dev/null 2>&1 \
|
|
117
|
+
|| die "house '$CH_DB' does not exist and cannot be created as $CH_USER"
|
|
118
|
+
fi
|
|
119
|
+
info "house '$CH_DB' ready"
|
|
120
|
+
|
|
121
|
+
# ---------- 5. write ~/.memhouse/env ----------------------------------------
|
|
122
|
+
mkdir -p "$ENV_DIR"; chmod 700 "$ENV_DIR"
|
|
123
|
+
# Values are single-quoted (embedded ' escaped) — this file is sourced by shells
|
|
124
|
+
# (docs/skills use `. ~/.memhouse/env`), so unquoted metacharacters in a password
|
|
125
|
+
# would corrupt the value or execute on source.
|
|
126
|
+
shq() { printf "%s" "$1" | sed "s/'/'\\\\''/g"; }
|
|
127
|
+
cat > "$ENV_FILE" <<EOF
|
|
128
|
+
# mem-house connection (written by install.sh $(date +%Y-%m-%d-%H_%M)). NEVER commit.
|
|
129
|
+
MEMHOUSE_URL='$(shq "$CH_URL")'
|
|
130
|
+
MEMHOUSE_USER='$(shq "$CH_USER")'
|
|
131
|
+
MEMHOUSE_PASSWORD='$(shq "$CH_PASSWORD")'
|
|
132
|
+
MEMHOUSE_DB='$(shq "$CH_DB")'
|
|
133
|
+
MEMHOUSE_PORT='$(shq "$DASH_PORT")'
|
|
134
|
+
EOF
|
|
135
|
+
chmod 600 "$ENV_FILE"
|
|
136
|
+
info "wrote $ENV_FILE"
|
|
137
|
+
|
|
138
|
+
export MEMHOUSE_URL="$CH_URL" MEMHOUSE_USER="$CH_USER" \
|
|
139
|
+
MEMHOUSE_PASSWORD="$CH_PASSWORD" MEMHOUSE_DB="$CH_DB" \
|
|
140
|
+
MEMHOUSE_PORT="$DASH_PORT"
|
|
141
|
+
|
|
142
|
+
# ---------- 6. schema + first ship ------------------------------------------
|
|
143
|
+
info "applying house schema (ship.js --ensure-schema)"
|
|
144
|
+
node "$SHIP" --ensure-schema \
|
|
145
|
+
|| die "ensure-schema failed. If $CH_USER cannot CREATE DATABASE, create '$CH_DB' first (or, on a kernel realm, provision the agency: see delivery/kernel-install.md), then re-run."
|
|
146
|
+
|
|
147
|
+
if [ "$NO_SHIP" -eq 0 ]; then
|
|
148
|
+
info "first ship (this parses local editor sessions and may take a while)"
|
|
149
|
+
node "$SHIP"
|
|
150
|
+
else
|
|
151
|
+
info "skipping first ship (--no-ship)"
|
|
152
|
+
fi
|
|
153
|
+
|
|
154
|
+
# ---------- 7. next steps ----------------------------------------------------
|
|
155
|
+
cat <<EOF
|
|
156
|
+
|
|
157
|
+
mem-house installed.
|
|
158
|
+
|
|
159
|
+
env file $ENV_FILE
|
|
160
|
+
house $CH_DB @ $CH_URL
|
|
161
|
+
|
|
162
|
+
Next steps:
|
|
163
|
+
|
|
164
|
+
# dashboard (agentlytics UI over the house) -> http://localhost:$DASH_PORT
|
|
165
|
+
node "$SERVER"
|
|
166
|
+
|
|
167
|
+
# keep shipping continuously (re-ships incrementally; Ctrl-C to stop)
|
|
168
|
+
node "$SHIP" --loop
|
|
169
|
+
# background it:
|
|
170
|
+
nohup node "$SHIP" --loop >> "$ENV_DIR/ship.log" 2>&1 &
|
|
171
|
+
|
|
172
|
+
# agent skills (search/sessions/sql from inside Claude Code):
|
|
173
|
+
node "$REPO_ROOT/bin/memhouse.js" plugins install claude
|
|
174
|
+
# -> installs memhouse-search / memhouse-sessions / memhouse-sql into
|
|
175
|
+
# \${CLAUDE_CONFIG_DIR:-\$HOME/.claude}/skills (loads next session)
|
|
176
|
+
|
|
177
|
+
Verify anytime:
|
|
178
|
+
set -a; . "$ENV_FILE"; set +a
|
|
179
|
+
curl -sS --user "\$MEMHOUSE_USER:\$MEMHOUSE_PASSWORD" --data-binary \\
|
|
180
|
+
"SELECT count() FROM sessions" "\$MEMHOUSE_URL/?database=\$MEMHOUSE_DB&final=1"
|
|
181
|
+
EOF
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# mem-house — install as a kernel agency
|
|
2
|
+
|
|
3
|
+
How to run mem-house as an **agency** on an ultimagent kernel — a ClickHouse server
|
|
4
|
+
(a **town**) promoted into a realm — instead of a plain ClickHouse. Same flow proven
|
|
5
|
+
for the agentlytics agency (`agency/AGENCY.md`); the agency/house name here is
|
|
6
|
+
**`memhouse`** (CH identifiers can't carry a dash). What lands is a **house** (the
|
|
7
|
+
`memhouse` database) with a **resident** (the shipper — `kind = "worker"`,
|
|
8
|
+
`on = "loop"`) — that pairing is the agency. Residents write; routines read, and the
|
|
9
|
+
`sessions_v` view is a routine. Terms: `../../TERMINOLOGY.md`.
|
|
10
|
+
|
|
11
|
+
This is an operator runbook, so it speaks machine vocabulary throughout — ClickHouse,
|
|
12
|
+
`GRANT`, row policies. That is correct for this audience.
|
|
13
|
+
|
|
14
|
+
## 1. Provision (operator, one-time — from the kernel checkout)
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
python3 executor/executor.py provision memhouse
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Submits `install-agency{name:'memhouse'}`; the mayor — the human owner — approves;
|
|
21
|
+
the executor (running as the ego, the `kernel` user) creates database `memhouse`
|
|
22
|
+
(the house), owner `memhouse_root`
|
|
23
|
+
(`realm_user` profile), and owner grants. It returns a one-time **credential** —
|
|
24
|
+
rotate on first connect (`issue-credential{user:'memhouse_root'}`).
|
|
25
|
+
|
|
26
|
+
## 2. Owner sets up the house
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npm install -g memhouse --allow-scripts=better-sqlite3
|
|
30
|
+
memhouse install --yes \
|
|
31
|
+
--url https://<kernel-host>:8443 \
|
|
32
|
+
--user memhouse_root \
|
|
33
|
+
--password <credential-from-provision> \
|
|
34
|
+
--db memhouse
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
That writes `~/.memhouse/env`, applies the schema, and runs the first ship.
|
|
38
|
+
`memhouse start` then keeps it fresh on a 300s loop.
|
|
39
|
+
|
|
40
|
+
Keep `--allow-scripts=better-sqlite3`: without it npm 12 leaves `better-sqlite3`
|
|
41
|
+
with no native binding and the five SQLite-backed adapters ship nothing, silently.
|
|
42
|
+
`memhouse discover` names any adapter it had to skip.
|
|
43
|
+
|
|
44
|
+
The kernel never runs or reads any of this — it is content-blind; the shipper is
|
|
45
|
+
the agency's own resident, a deterministic `worker` on a loop (no LLM, not an
|
|
46
|
+
injection surface). It is the only thing here that writes without being asked.
|
|
47
|
+
|
|
48
|
+
## 3. Members (multi-user)
|
|
49
|
+
|
|
50
|
+
For each person joining, split across the two authorities:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
# The EGO (the `kernel` user) mints the identity; the mayor approves:
|
|
54
|
+
python3 executor/executor.py submit register-member '{"handle":"alice"}'
|
|
55
|
+
python3 executor/executor.py approve <call_id> && python3 executor/executor.py drain
|
|
56
|
+
|
|
57
|
+
# OWNER grants house access (memhouse_root has grant-option on memhouse.*):
|
|
58
|
+
# GRANT INSERT, SELECT ON memhouse.* TO alice
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Each member then runs the shipper with **their own** credential
|
|
62
|
+
(`MEMHOUSE_USER=alice`) — the house stamps `user_id='alice'` on their rows,
|
|
63
|
+
un-spoofably (`MATERIALIZED currentUser()`, `async_insert=0`).
|
|
64
|
+
|
|
65
|
+
## 4. Visibility: own-only vs team pool
|
|
66
|
+
|
|
67
|
+
- **Own-only** (memory-house's model — each member sees only their own rows): the
|
|
68
|
+
**ego (the `kernel` user) or the mayor** applies `mem-house/rls.sql` (three row
|
|
69
|
+
policies bound to the `member` role). The owner cannot — `CREATE ROW POLICY` needs
|
|
70
|
+
ACCESS MANAGEMENT, which the kernel withholds from agency owners by design.
|
|
71
|
+
- **Team pool** (everyone sees everything): apply no policy; the owner GRANTs from
|
|
72
|
+
step 3 are the whole model.
|
|
73
|
+
|
|
74
|
+
Pick one; do not mix on the same role.
|
|
75
|
+
|
|
76
|
+
## 5. Verify
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
memhouse stats # per-source counts as the owner
|
|
80
|
+
# as a member (own-only): counts reflect only that member's rows
|
|
81
|
+
curl -s -u "alice:<pw>" "$MEMHOUSE_URL/?database=memhouse" \
|
|
82
|
+
--data-binary "SELECT count() FROM sessions_v SETTINGS final=1"
|
|
83
|
+
```
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "memhouse",
|
|
3
|
+
"description": "Conversation memory for AI agents over the memhouse typed ClickHouse store: search, browse, and analyze every past session across all 17 supported editors and machines. Skills install as /memhouse:search, /memhouse:sessions, and /memhouse:sql.",
|
|
4
|
+
"version": "0.3.0",
|
|
5
|
+
"author": { "name": "Ramazan Polat" },
|
|
6
|
+
"homepage": "https://github.com/agent-realm/memhouse",
|
|
7
|
+
"keywords": ["memory", "clickhouse", "search", "sessions", "analytics", "agentlytics"]
|
|
8
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: memhouse-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". 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(set -a*), Bash(. *), Bash(curl*)
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# memhouse-search — search conversation memory
|
|
10
|
+
|
|
11
|
+
Search the full message history in the memhouse house (`messages` + `sessions`
|
|
12
|
+
tables on ClickHouse).
|
|
13
|
+
|
|
14
|
+
## Connection
|
|
15
|
+
|
|
16
|
+
Credentials come from `~/.memhouse/env` (or already-exported `MEMHOUSE_*` vars).
|
|
17
|
+
Every query runs over ClickHouse HTTP with `final=1` (ReplacingMergeTree keeps
|
|
18
|
+
stale row versions until merges; `final=1` collapses to latest-wins — always
|
|
19
|
+
include it on reads):
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
set -a; [ -f ~/.memhouse/env ] && . ~/.memhouse/env; set +a
|
|
23
|
+
curl -sS --fail-with-body --user "${MEMHOUSE_USER:-memhouse_root}:${MEMHOUSE_PASSWORD:-}" \
|
|
24
|
+
--data-binary @- "${MEMHOUSE_URL:-http://localhost:8123}/?database=${MEMHOUSE_DB:-memhouse}&final=1" <<'SQL'
|
|
25
|
+
<the query>
|
|
26
|
+
FORMAT PrettyCompact
|
|
27
|
+
SQL
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
`FORMAT PrettyCompact` for display; `FORMAT JSONEachRow` when you want to parse
|
|
31
|
+
the rows yourself.
|
|
32
|
+
|
|
33
|
+
## How to search (the FTS columns)
|
|
34
|
+
|
|
35
|
+
`messages` carries two MATERIALIZED lowercase copies of `text`, each with a
|
|
36
|
+
text index — search those, display `text`:
|
|
37
|
+
|
|
38
|
+
- `text_ngram` (ngram index) → substring match: `text_ngram LIKE '%term%'`
|
|
39
|
+
- `text_word` (word index) → whole-token match: `hasToken(text_word, 'term')`
|
|
40
|
+
|
|
41
|
+
**Search terms MUST be lowercased** (the columns are `lower(text)`); escape
|
|
42
|
+
single quotes in user terms as `\'`. Multiple words → AND of clauses; a quoted
|
|
43
|
+
phrase → one `LIKE` clause for the whole phrase.
|
|
44
|
+
|
|
45
|
+
### Example 1 — substring search with session names
|
|
46
|
+
|
|
47
|
+
```sql
|
|
48
|
+
SELECT m.ts, m.source, m.project, m.role, m.session_id,
|
|
49
|
+
s.name AS session,
|
|
50
|
+
substring(m.text, 1, 300) AS snippet
|
|
51
|
+
FROM messages AS m
|
|
52
|
+
LEFT JOIN sessions AS s USING (session_id)
|
|
53
|
+
WHERE m.text_ngram LIKE '%postgres%' AND m.text_ngram LIKE '%migration%'
|
|
54
|
+
ORDER BY m.ts DESC
|
|
55
|
+
LIMIT 30
|
|
56
|
+
FORMAT PrettyCompact
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Example 2 — word match, scoped by editor and time
|
|
60
|
+
|
|
61
|
+
```sql
|
|
62
|
+
SELECT ts, project, session_id, substring(text, 1, 200) AS snippet
|
|
63
|
+
FROM messages
|
|
64
|
+
WHERE hasToken(text_word, 'rls')
|
|
65
|
+
AND source = 'claude-code'
|
|
66
|
+
AND ts > now() - INTERVAL 14 DAY
|
|
67
|
+
ORDER BY ts DESC
|
|
68
|
+
LIMIT 20
|
|
69
|
+
FORMAT PrettyCompact
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Example 3 — which sessions mention it most (then drill in)
|
|
73
|
+
|
|
74
|
+
```sql
|
|
75
|
+
SELECT m.session_id, any(s.name) AS session, any(m.project) AS project,
|
|
76
|
+
count() AS hits, max(m.ts) AS last_hit
|
|
77
|
+
FROM messages AS m
|
|
78
|
+
LEFT JOIN sessions AS s USING (session_id)
|
|
79
|
+
WHERE m.text_ngram LIKE '%clickhouse cache%'
|
|
80
|
+
GROUP BY m.session_id
|
|
81
|
+
ORDER BY hits DESC
|
|
82
|
+
LIMIT 10
|
|
83
|
+
FORMAT PrettyCompact
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Qualifiers
|
|
87
|
+
|
|
88
|
+
- `in <project>` → `AND m.project ILIKE '%<project>%'`
|
|
89
|
+
- `last N days` → `AND m.ts > now() - INTERVAL N DAY`
|
|
90
|
+
- `from <editor>` → `AND m.source = '<id>'` (ids: `claude-code`, `codex`,
|
|
91
|
+
`cursor`, `cursor-agent`, `vscode`, `zed`, `opencode`, `gemini-cli`,
|
|
92
|
+
`windsurf`, `antigravity`, `copilot-cli`, `goose`, `kiro`, …)
|
|
93
|
+
- Skip subagent noise → `AND NOT m.is_subagent`
|
|
94
|
+
- Only user/assistant text lives in `messages`; tool invocations are in
|
|
95
|
+
`tool_calls` (`tool_name`, `args`) — search `args ILIKE '%term%'` there only
|
|
96
|
+
if the user explicitly wants tool calls searched.
|
|
97
|
+
|
|
98
|
+
## Output
|
|
99
|
+
|
|
100
|
+
Summarize hits grouped by session (name, source, project, date, 1–2 best
|
|
101
|
+
snippets each) and give the total hit count. Offer to pull the full transcript
|
|
102
|
+
of the best session (`SELECT role, text FROM messages WHERE session_id = '…'
|
|
103
|
+
ORDER BY seq`). If zero hits, retry with broader/fewer terms and report the
|
|
104
|
+
corpus size (`SELECT count() FROM messages`).
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: memhouse-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(set -a*), Bash(. *), Bash(curl*)
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# memhouse-sessions — browse session history
|
|
10
|
+
|
|
11
|
+
List/filter sessions from the `sessions_v` rollup view (session metadata +
|
|
12
|
+
message aggregates) in the memhouse house.
|
|
13
|
+
|
|
14
|
+
## When to use
|
|
15
|
+
|
|
16
|
+
- The user wants an overview of recent activity rather than a specific quote.
|
|
17
|
+
- As a first step before digging into one session, when they don't have an
|
|
18
|
+
id/name yet.
|
|
19
|
+
|
|
20
|
+
## Connection
|
|
21
|
+
|
|
22
|
+
Credentials from `~/.memhouse/env` (or exported `MEMHOUSE_*`). Always read with
|
|
23
|
+
`final=1` (collapses ReplacingMergeTree duplicates to latest-wins):
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
set -a; [ -f ~/.memhouse/env ] && . ~/.memhouse/env; set +a
|
|
27
|
+
curl -sS --fail-with-body --user "${MEMHOUSE_USER:-memhouse_root}:${MEMHOUSE_PASSWORD:-}" \
|
|
28
|
+
--data-binary @- "${MEMHOUSE_URL:-http://localhost:8123}/?database=${MEMHOUSE_DB:-memhouse}&final=1" <<'SQL'
|
|
29
|
+
<the query>
|
|
30
|
+
FORMAT PrettyCompact
|
|
31
|
+
SQL
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Default listing
|
|
35
|
+
|
|
36
|
+
```sql
|
|
37
|
+
SELECT session_id, name, source, host, project,
|
|
38
|
+
started, ended, total_msgs, user_msgs, assistant_msgs,
|
|
39
|
+
output_tokens,
|
|
40
|
+
substring(first_prompt, 1, 80) AS first_prompt
|
|
41
|
+
FROM sessions_v
|
|
42
|
+
ORDER BY ended DESC
|
|
43
|
+
LIMIT 20
|
|
44
|
+
FORMAT PrettyCompact
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
- Default `LIMIT 20`; `[N]` overrides.
|
|
48
|
+
- `today` / `week` / `month` → `WHERE ended > now() - INTERVAL 1 DAY`
|
|
49
|
+
(`7 DAY` / `30 DAY`).
|
|
50
|
+
- `project <name>` → `WHERE project ILIKE '%<name>%'`.
|
|
51
|
+
- `from <editor>` → `WHERE source = '<id>'` (`claude-code`, `codex`, `cursor`,
|
|
52
|
+
`vscode`, `zed`, `opencode`, `gemini-cli`, `windsurf`, …).
|
|
53
|
+
- Unnamed sessions (`name = ''`): fall back to `first_prompt` as the label.
|
|
54
|
+
|
|
55
|
+
## More cuts
|
|
56
|
+
|
|
57
|
+
Per-editor rollup ("where has my work been happening?"):
|
|
58
|
+
|
|
59
|
+
```sql
|
|
60
|
+
SELECT source, count() AS sessions, sum(total_msgs) AS msgs,
|
|
61
|
+
sum(output_tokens) AS out_tokens, max(ended) AS last_activity
|
|
62
|
+
FROM sessions_v
|
|
63
|
+
GROUP BY source
|
|
64
|
+
ORDER BY sessions DESC
|
|
65
|
+
FORMAT PrettyCompact
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Busiest projects this month:
|
|
69
|
+
|
|
70
|
+
```sql
|
|
71
|
+
SELECT project, count() AS sessions, sum(assistant_msgs) AS assistant_msgs
|
|
72
|
+
FROM sessions_v
|
|
73
|
+
WHERE ended > now() - INTERVAL 30 DAY AND project != ''
|
|
74
|
+
GROUP BY project
|
|
75
|
+
ORDER BY sessions DESC
|
|
76
|
+
LIMIT 15
|
|
77
|
+
FORMAT PrettyCompact
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Examples
|
|
81
|
+
|
|
82
|
+
| User says | What you do |
|
|
83
|
+
|---|---|
|
|
84
|
+
| "what was I working on yesterday?" | `WHERE ended > now() - INTERVAL 1 DAY` |
|
|
85
|
+
| "show my last 10 sessions in agentlytics" | `LIMIT 10 … WHERE project ILIKE '%agentlytics%'` |
|
|
86
|
+
| "list my cursor sessions this month" | `WHERE source = 'cursor' AND ended > now() - INTERVAL 30 DAY` |
|
|
87
|
+
| "which machine did I do the schema work on?" | search via memhouse-search, or filter `host` here |
|
|
88
|
+
|
|
89
|
+
## Output
|
|
90
|
+
|
|
91
|
+
A compact table: when, name (or first prompt), source, host, project, msgs,
|
|
92
|
+
tokens. Offer to pull a full transcript next
|
|
93
|
+
(`SELECT role, text FROM messages WHERE session_id = '…' ORDER BY seq`) or to
|
|
94
|
+
run memhouse-search for a specific quote.
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: memhouse-sql
|
|
3
|
+
description: Run free-form read-only SQL against memhouse conversation memory (typed sessions/messages/tool_calls tables + the sessions_v view on ClickHouse). Use for ad-hoc analytics the other memhouse skills don't cover — token spend, model/editor usage, tool rankings, activity heatmaps, busiest days/projects, cache-hit ratios, or any custom question over conversation data.
|
|
4
|
+
user-invocable: true
|
|
5
|
+
argument-hint: "<question or SQL>"
|
|
6
|
+
allowed-tools: Bash(set -a*), Bash(. *), Bash(curl*)
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# memhouse-sql — ad-hoc analytics
|
|
10
|
+
|
|
11
|
+
If the user gives SQL, run it (append `FORMAT PrettyCompact` if no FORMAT
|
|
12
|
+
given). If they give a question, write the SQL yourself from the schema below.
|
|
13
|
+
|
|
14
|
+
**Read-only rule:** the shipper (`ship.js`) is the only writer. Never INSERT/
|
|
15
|
+
ALTER/DROP from here — member credentials typically hold only
|
|
16
|
+
`INSERT, SELECT ON memhouse.*`, and on shared instances own-only row policies
|
|
17
|
+
scope reads to your rows.
|
|
18
|
+
|
|
19
|
+
## Connection
|
|
20
|
+
|
|
21
|
+
Credentials from `~/.memhouse/env` (or exported `MEMHOUSE_*`). Always read with
|
|
22
|
+
`final=1` — every table is `ReplacingMergeTree(ingested_at)`, so without it you
|
|
23
|
+
can double-count stale row versions:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
set -a; [ -f ~/.memhouse/env ] && . ~/.memhouse/env; set +a
|
|
27
|
+
curl -sS --fail-with-body --user "${MEMHOUSE_USER:-memhouse_root}:${MEMHOUSE_PASSWORD:-}" \
|
|
28
|
+
--data-binary @- "${MEMHOUSE_URL:-http://localhost:8123}/?database=${MEMHOUSE_DB:-memhouse}&final=1" <<'SQL'
|
|
29
|
+
<the query>
|
|
30
|
+
FORMAT PrettyCompact
|
|
31
|
+
SQL
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
`FORMAT PrettyCompact` for display, `FORMAT JSONEachRow` to parse.
|
|
35
|
+
|
|
36
|
+
## Schema (the house)
|
|
37
|
+
|
|
38
|
+
| Object | Kind | Columns |
|
|
39
|
+
|---|---|---|
|
|
40
|
+
| `sessions` | table, 1 row/session | `session_id, source, host, name, mode, folder, project, git_branch, created_at, last_updated_at, message_count, path, extra JSON, user_id, ingested_at` |
|
|
41
|
+
| `messages` | table, 1 row/message | `session_id, seq, source, host, ts, role, model, input_tokens, output_tokens, cache_read_tokens, cache_write_tokens, text, project, folder, is_subagent, extra JSON, line_hash, user_id, ingested_at` + FTS columns `text_ngram`/`text_word` (lowercased; see memhouse-search) |
|
|
42
|
+
| `tool_calls` | table, 1 row/tool call | `session_id, seq, idx, source, host, tool_name, args, ts, project, folder, user_id, ingested_at` |
|
|
43
|
+
| `sessions_v` | view, rollup | `session_id, source, host, name, mode, folder, project, git_branch, user_id, created_at, last_updated_at, started, ended, duration_sec, total_msgs, user_msgs, assistant_msgs, subagent_msgs, models, input_tokens, output_tokens, cache_read_tokens, cache_write_tokens, user_chars, assistant_chars, first_prompt` |
|
|
44
|
+
|
|
45
|
+
Notes: `source` = editor id (`claude-code`, `codex`, `cursor`, `cursor-agent`,
|
|
46
|
+
`vscode`, `zed`, `opencode`, `gemini-cli`, `windsurf`, `antigravity`, …);
|
|
47
|
+
`seq` = message index within its session (0-based); `tool_calls.idx` = call
|
|
48
|
+
index within the session; `user_id` is server-stamped (`currentUser()`);
|
|
49
|
+
`models` in `sessions_v` is an `Array(String)`. Exclude `''` and
|
|
50
|
+
`'<synthetic>'` from model aggregates.
|
|
51
|
+
|
|
52
|
+
## Examples
|
|
53
|
+
|
|
54
|
+
Token spend by model:
|
|
55
|
+
|
|
56
|
+
```sql
|
|
57
|
+
SELECT model, sum(input_tokens) AS in_tok, sum(output_tokens) AS out_tok,
|
|
58
|
+
sum(cache_read_tokens) AS cache_read
|
|
59
|
+
FROM messages
|
|
60
|
+
WHERE model NOT IN ('', '<synthetic>')
|
|
61
|
+
GROUP BY model
|
|
62
|
+
ORDER BY out_tok DESC
|
|
63
|
+
FORMAT PrettyCompact
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Which tools do I use most (memhouse exclusive — memory-house has no tool table):
|
|
67
|
+
|
|
68
|
+
```sql
|
|
69
|
+
SELECT tool_name, count() AS calls, uniqExact(session_id) AS sessions
|
|
70
|
+
FROM tool_calls
|
|
71
|
+
GROUP BY tool_name
|
|
72
|
+
ORDER BY calls DESC
|
|
73
|
+
LIMIT 20
|
|
74
|
+
FORMAT PrettyCompact
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Busiest days, last two weeks:
|
|
78
|
+
|
|
79
|
+
```sql
|
|
80
|
+
SELECT toDate(ts) AS day, uniqExact(session_id) AS sessions, count() AS msgs
|
|
81
|
+
FROM messages
|
|
82
|
+
WHERE ts > now() - INTERVAL 14 DAY
|
|
83
|
+
GROUP BY day
|
|
84
|
+
ORDER BY day DESC
|
|
85
|
+
FORMAT PrettyCompact
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Activity by editor this week / subagent share:
|
|
89
|
+
|
|
90
|
+
```sql
|
|
91
|
+
SELECT source, count() AS msgs, countIf(is_subagent) AS subagent_msgs
|
|
92
|
+
FROM messages
|
|
93
|
+
WHERE ts > now() - INTERVAL 7 DAY
|
|
94
|
+
GROUP BY source
|
|
95
|
+
ORDER BY msgs DESC
|
|
96
|
+
FORMAT PrettyCompact
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Longest sessions by wall-clock:
|
|
100
|
+
|
|
101
|
+
```sql
|
|
102
|
+
SELECT name, project, source, duration_sec, total_msgs, output_tokens
|
|
103
|
+
FROM sessions_v
|
|
104
|
+
ORDER BY duration_sec DESC
|
|
105
|
+
LIMIT 10
|
|
106
|
+
FORMAT PrettyCompact
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Output
|
|
110
|
+
|
|
111
|
+
Run the query, present the result as a compact table, and add one or two lines
|
|
112
|
+
of interpretation. Offer a natural follow-up cut (by source, by project, by
|
|
113
|
+
host, by token volume) when relevant. Dollar costs are computed by the
|
|
114
|
+
dashboard server from pricing.js — token sums here are the raw material, not
|
|
115
|
+
dollars.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
-- mem-house — own-only row-level security (the sharing model's isolation half).
|
|
2
|
+
--
|
|
3
|
+
-- Applied by the KERNEL (or the mayor, who has ACCESS MANAGEMENT) — the agency owner
|
|
4
|
+
-- cannot CREATE ROW POLICY by design. Binds to the kernel's `member` role, so every
|
|
5
|
+
-- registered member sees only their own rows; the owner (not a member) sees the whole
|
|
6
|
+
-- house. For a shared team pool instead, apply NO policy — just the owner's GRANTs.
|
|
7
|
+
--
|
|
8
|
+
-- Substitute `memhouse` if the operator named the house differently. Policies bind to
|
|
9
|
+
-- physical tables; views (sessions_v) inherit the querying user's policy via messages/
|
|
10
|
+
-- sessions.
|
|
11
|
+
|
|
12
|
+
CREATE ROW POLICY IF NOT EXISTS own_sessions ON memhouse.sessions
|
|
13
|
+
FOR SELECT USING user_id = currentUser() TO member;
|
|
14
|
+
CREATE ROW POLICY IF NOT EXISTS own_messages ON memhouse.messages
|
|
15
|
+
FOR SELECT USING user_id = currentUser() TO member;
|
|
16
|
+
CREATE ROW POLICY IF NOT EXISTS own_tool_calls ON memhouse.tool_calls
|
|
17
|
+
FOR SELECT USING user_id = currentUser() TO member;
|