memhouse 0.3.0 → 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/README.md +18 -5
- package/bin/memhouse.js +75 -3
- package/editors/adapter-errors.js +52 -0
- package/editors/antigravity.js +32 -3
- package/editors/cursor.js +67 -7
- package/editors/goose.js +35 -7
- package/editors/index.js +97 -2
- package/editors/opencode.js +71 -13
- package/editors/zed.js +20 -4
- package/mem-house/delivery/AGENT-INSTALL.md +35 -23
- package/mem-house/delivery/kernel-install.md +15 -8
- package/mem-house/shipper/ship.js +63 -4
- package/package.json +5 -2
- package/public/assets/{index-BsgjOSTv.js → index-CCPh7a2Z.js} +2 -2
- package/public/index.html +1 -1
- package/ui/src/pages/SqlViewer.jsx +35 -9
package/public/index.html
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
8
8
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
9
9
|
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@300;400;500;600;700&display=swap" rel="stylesheet" />
|
|
10
|
-
<script type="module" crossorigin src="/assets/index-
|
|
10
|
+
<script type="module" crossorigin src="/assets/index-CCPh7a2Z.js"></script>
|
|
11
11
|
<link rel="stylesheet" crossorigin href="/assets/index-DV6ONi_F.css">
|
|
12
12
|
</head>
|
|
13
13
|
<body>
|
|
@@ -21,7 +21,10 @@ const TYPED_EXAMPLES = [
|
|
|
21
21
|
{ label: 'Token usage by editor', sql: `SELECT source, sum(input_tokens) AS input_tokens, sum(output_tokens) AS output_tokens FROM sessions_v GROUP BY source ORDER BY input_tokens DESC` },
|
|
22
22
|
{ label: 'Sessions by mode', sql: `SELECT mode, count() AS count FROM sessions_v WHERE mode != '' GROUP BY mode ORDER BY count DESC` },
|
|
23
23
|
{ label: 'Hourly distribution', sql: `SELECT toHour(ts) AS hour, count() AS count FROM messages GROUP BY hour ORDER BY hour` },
|
|
24
|
-
|
|
24
|
+
// Grouped by (session_id, user_id), matching the table's ORDER BY. In a team-pool
|
|
25
|
+
// house two members can ship the same adapter-local session id; grouping on
|
|
26
|
+
// session_id alone would merge them and make any(source) an arbitrary member's.
|
|
27
|
+
{ label: 'Full-text search', sql: `SELECT session_id, user_id, any(source) AS source, count() AS hits FROM messages WHERE text_ngram LIKE '%clickhouse%' GROUP BY session_id, user_id ORDER BY hits DESC LIMIT 10` },
|
|
25
28
|
]
|
|
26
29
|
const LEGACY_EXAMPLES = [
|
|
27
30
|
{ label: 'Sessions per editor', sql: `SELECT source, count() AS count FROM chats GROUP BY source ORDER BY count DESC` },
|
|
@@ -36,8 +39,13 @@ const LEGACY_EXAMPLES = [
|
|
|
36
39
|
|
|
37
40
|
export default function SqlViewer() {
|
|
38
41
|
const { dark } = useTheme()
|
|
39
|
-
|
|
40
|
-
|
|
42
|
+
// Three states, and they must stay distinguishable: 'loading' while /api/schema is
|
|
43
|
+
// in flight, an array once the house's flavor is known, and null when the request
|
|
44
|
+
// finished without telling us. Guessing wrong offers queries against tables that do
|
|
45
|
+
// not exist, so nothing is offered until the answer is in — but a page that is
|
|
46
|
+
// merely still loading must not claim detection failed.
|
|
47
|
+
const [examples, setExamples] = useState('loading')
|
|
48
|
+
const [sql, setSql] = useState('')
|
|
41
49
|
const [result, setResult] = useState(null)
|
|
42
50
|
const [error, setError] = useState(null)
|
|
43
51
|
const [loading, setLoading] = useState(false)
|
|
@@ -50,15 +58,26 @@ export default function SqlViewer() {
|
|
|
50
58
|
useEffect(() => {
|
|
51
59
|
fetchSchema().then(s => {
|
|
52
60
|
setSchema(s)
|
|
53
|
-
//
|
|
54
|
-
|
|
61
|
+
// Three outcomes, not two. The typed house has the sessions_v rollup; the
|
|
62
|
+
// legacy one has chats. Anything else — an error object with no `tables`, a
|
|
63
|
+
// half-applied schema with neither marker — is indeterminate, and offering
|
|
64
|
+
// either set would hand the user queries that error.
|
|
65
|
+
const tables = s?.tables
|
|
66
|
+
const ex = !Array.isArray(tables) ? null
|
|
67
|
+
: tables.includes('sessions_v') ? TYPED_EXAMPLES
|
|
68
|
+
: tables.includes('chats') ? LEGACY_EXAMPLES
|
|
69
|
+
: null
|
|
55
70
|
setExamples(ex)
|
|
71
|
+
if (!ex) return
|
|
56
72
|
// Swap the prefilled query only if the user hasn't edited it yet.
|
|
57
|
-
setSql(prev => (prev === TYPED_EXAMPLES[0].sql || prev === LEGACY_EXAMPLES[0].sql) ? ex[0].sql : prev)
|
|
58
|
-
}).catch(() =>
|
|
73
|
+
setSql(prev => (prev === '' || prev === TYPED_EXAMPLES[0].sql || prev === LEGACY_EXAMPLES[0].sql) ? ex[0].sql : prev)
|
|
74
|
+
}).catch(() => setExamples(null))
|
|
59
75
|
}, [])
|
|
60
76
|
|
|
61
77
|
const runQuery = async () => {
|
|
78
|
+
// Guard here, not only on the button: ⌘+Enter reaches this directly, and an
|
|
79
|
+
// empty submit comes back as a bare "sql string required" from /api/query.
|
|
80
|
+
if (!sql.trim() || loading) return
|
|
62
81
|
setLoading(true)
|
|
63
82
|
setError(null)
|
|
64
83
|
setResult(null)
|
|
@@ -195,9 +214,16 @@ export default function SqlViewer() {
|
|
|
195
214
|
</div>
|
|
196
215
|
)}
|
|
197
216
|
|
|
198
|
-
{/* Example queries
|
|
217
|
+
{/* Example queries — withheld until the schema flavor is known, since the
|
|
218
|
+
typed and legacy sets target entirely different tables. */}
|
|
199
219
|
<div className="flex flex-wrap gap-1">
|
|
200
|
-
{examples
|
|
220
|
+
{examples === 'loading' ? (
|
|
221
|
+
<span className="text-[11px]" style={txt2Style}>loading examples…</span>
|
|
222
|
+
) : examples === null ? (
|
|
223
|
+
<span className="text-[11px]" style={txt2Style}>
|
|
224
|
+
examples unavailable — could not determine the schema from /api/schema
|
|
225
|
+
</span>
|
|
226
|
+
) : examples.map((q, i) => (
|
|
201
227
|
<button
|
|
202
228
|
key={i}
|
|
203
229
|
onClick={() => setSql(q.sql)}
|