memhouse 0.4.2 → 0.4.3
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/bin/memhouse.js +132 -1
- package/editors/antigravity.js +11 -2
- package/editors/cursor.js +16 -7
- package/editors/opencode.js +12 -3
- package/editors/zed.js +13 -4
- package/mem-house/delivery/AGENT-INSTALL-PROMPT.md +58 -0
- package/mem-house/delivery/AGENT-INSTALL.md +7 -1
- package/package.json +1 -1
package/bin/memhouse.js
CHANGED
|
@@ -320,6 +320,8 @@ Data ship one incremental pass (--full | --loop [sec])
|
|
|
320
320
|
|
|
321
321
|
Agents plugins list | install claude [--target DIR] | remove claude
|
|
322
322
|
prompt print the memory system-prompt snippet
|
|
323
|
+
prompt --install print an install prompt for an agent, with this
|
|
324
|
+
machine's state and the one route that applies
|
|
323
325
|
|
|
324
326
|
House deploy --local run ClickHouse in docker/podman, then install
|
|
325
327
|
deploy --down remove the local house (container + volume)
|
|
@@ -394,6 +396,129 @@ async function cmdDiscover() {
|
|
|
394
396
|
return out;
|
|
395
397
|
}
|
|
396
398
|
|
|
399
|
+
/**
|
|
400
|
+
* Render the agent install prompt with this machine's facts.
|
|
401
|
+
*
|
|
402
|
+
* The value is in what it removes: the CLI has already probed for a house, an engine and
|
|
403
|
+
* an existing config, so the prompt can state the ONE route that applies. A static
|
|
404
|
+
* runbook makes the agent derive that from a decision tree, which is where it picks the
|
|
405
|
+
* wrong branch — most expensively by asking a user with no ClickHouse for a ClickHouse
|
|
406
|
+
* URL, which is exactly what onboard itself used to do.
|
|
407
|
+
*/
|
|
408
|
+
async function renderInstallPrompt() {
|
|
409
|
+
const tplPath = path.join(DELIVERY, 'AGENT-INSTALL-PROMPT.md');
|
|
410
|
+
const tpl = fs.readFileSync(tplPath, 'utf-8');
|
|
411
|
+
const body = tpl.split('<!-- PROMPT BODY BELOW -->')[1] || tpl;
|
|
412
|
+
|
|
413
|
+
const cfg = resolveConfig();
|
|
414
|
+
const configured = fs.existsSync(ENV_FILE);
|
|
415
|
+
const engines = deployableEngines();
|
|
416
|
+
|
|
417
|
+
// Probe rather than assume. JSON_OUT is forced off so cmdDiscover returns its object
|
|
418
|
+
// instead of printing JSON and returning undefined.
|
|
419
|
+
const saved = JSON_OUT;
|
|
420
|
+
let found = null;
|
|
421
|
+
try {
|
|
422
|
+
found = await withSuppressedOutput(() => cmdDiscover());
|
|
423
|
+
} catch { /* fall through to the unknown-state wording */ } finally { void saved; }
|
|
424
|
+
|
|
425
|
+
const editors = (found && found.editors || []).filter((e) => e.sessions > 0);
|
|
426
|
+
const reachable = (found && found.clickhouse || []).filter((p) => p.reachable);
|
|
427
|
+
const houseUp = reachable.length > 0;
|
|
428
|
+
|
|
429
|
+
// Is an existing install actually working, or configured-but-broken? A broken one is
|
|
430
|
+
// the case a plain "install it" prompt handles worst.
|
|
431
|
+
let state = 'not installed';
|
|
432
|
+
if (configured) {
|
|
433
|
+
let ok0 = false;
|
|
434
|
+
try { ok0 = !!(await ch(cfg, 'SELECT 1')); } catch { ok0 = false; }
|
|
435
|
+
state = ok0 ? 'installed and connected' : 'configured, but the house does not answer';
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
let plan;
|
|
439
|
+
if (state === 'installed and connected') {
|
|
440
|
+
plan = `memhouse is already installed and connected to \`${cfg.url}\` (database \`${cfg.db}\`).
|
|
441
|
+
**Do not reinstall.** Verify and hand back:
|
|
442
|
+
|
|
443
|
+
1. \`memhouse doctor\` — every line should be a check mark.
|
|
444
|
+
2. \`memhouse status\` — note the session and message counts.
|
|
445
|
+
3. If the shipper is not running: \`memhouse start\`.
|
|
446
|
+
4. If \`doctor\` reports rooms missing, the house may predate 0.4.0; it will say so by
|
|
447
|
+
name and print the recovery. Follow what it prints rather than reinstalling.`;
|
|
448
|
+
} else if (state.startsWith('configured')) {
|
|
449
|
+
plan = `memhouse is configured to use \`${cfg.url}\` (database \`${cfg.db}\`) but that
|
|
450
|
+
endpoint does not answer. Do NOT reconfigure before finding out why.
|
|
451
|
+
|
|
452
|
+
1. Is the house simply stopped? If it was a local container, \`memhouse deploy --local\`
|
|
453
|
+
brings the same volume back — it does not lose data.
|
|
454
|
+
2. Is it a remote house the user's network cannot reach right now? Ask them; do not
|
|
455
|
+
repoint their config at something else on your own initiative.
|
|
456
|
+
3. Only once the user says the old house is gone for good should you set up a new one,
|
|
457
|
+
and then say plainly that past sessions re-ship from local transcripts but anything
|
|
458
|
+
whose transcript was deleted lives only in the old house.`;
|
|
459
|
+
} else if (houseUp) {
|
|
460
|
+
plan = `A ClickHouse is already reachable at \`${reachable[0].url}\`. Use it.
|
|
461
|
+
|
|
462
|
+
1. \`npm install -g memhouse --allow-scripts=better-sqlite3\`
|
|
463
|
+
The flag is not optional — six adapters (cursor, zed, opencode, goose, windsurf,
|
|
464
|
+
antigravity) read SQLite stores and silently drop out without the native build.
|
|
465
|
+
2. Ask the user for the ClickHouse username and password for that endpoint.
|
|
466
|
+
3. \`memhouse install --url ${reachable[0].url} --user <user> --password <pw>\`
|
|
467
|
+
If they hold admin on it and want memhouse to create the user, database, rooms and
|
|
468
|
+
grants for them, use \`--admin-user\`/\`--admin-password\` instead; memhouse then
|
|
469
|
+
reconnects as the new member and never stores the admin credential.
|
|
470
|
+
If they would rather run the SQL themselves, \`memhouse install --print-sql\` prints
|
|
471
|
+
it and touches nothing.
|
|
472
|
+
4. \`memhouse start\`.`;
|
|
473
|
+
} else if (engines.length) {
|
|
474
|
+
plan = `There is no ClickHouse to point at, but **${engines.join(' and ')}** is available,
|
|
475
|
+
so memhouse can run one:
|
|
476
|
+
|
|
477
|
+
1. \`npm install -g memhouse --allow-scripts=better-sqlite3\`
|
|
478
|
+
The flag is not optional — six adapters read SQLite stores and silently drop out
|
|
479
|
+
without the native build.
|
|
480
|
+
2. \`memhouse deploy --local\`
|
|
481
|
+
This starts a loopback-only ClickHouse, generates a credential, installs, creates the
|
|
482
|
+
user's rooms and runs the first ship. One command, no questions.
|
|
483
|
+
3. \`memhouse start\`.
|
|
484
|
+
|
|
485
|
+
${process.platform === 'linux' && engines.includes('podman')
|
|
486
|
+
? 'On Linux with rootless podman the container stops at logout unless lingering is on.\nIf memhouse warns about this, run what it prints (`loginctl enable-linger <user>`)\nand tell the user why it matters.' : ''}`;
|
|
487
|
+
} else {
|
|
488
|
+
plan = `There is no ClickHouse reachable and no container engine to run one with. You
|
|
489
|
+
cannot finish this install alone — say so rather than improvising.
|
|
490
|
+
|
|
491
|
+
Tell the user they need one of:
|
|
492
|
+
- a ClickHouse they already run (Cloud, a server, a kernel house) plus its credentials;
|
|
493
|
+
- docker or podman installed, after which \`memhouse deploy --local\` does everything.
|
|
494
|
+
|
|
495
|
+
You can still do the harmless half now: \`npm install -g memhouse --allow-scripts=better-sqlite3\`,
|
|
496
|
+
then \`memhouse discover\` to show them what would be shipped once a house exists.`;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
const out = body
|
|
500
|
+
.replaceAll('{{VERSION}}', PKG.version)
|
|
501
|
+
.replaceAll('{{PLATFORM}}', `${process.platform}/${process.arch}`)
|
|
502
|
+
.replaceAll('{{NODE}}', process.versions.node)
|
|
503
|
+
.replaceAll('{{STATE}}', state)
|
|
504
|
+
.replaceAll('{{EDITORS}}', editors.length
|
|
505
|
+
? editors.map((e) => `${e.source} (${e.sessions})`).join(', ')
|
|
506
|
+
: 'none found — memhouse would ship nothing until an editor is used')
|
|
507
|
+
.replaceAll('{{HOUSE}}', houseUp ? reachable.map((p) => p.url).join(', ') : 'none reachable')
|
|
508
|
+
.replaceAll('{{ENGINES}}', engines.length ? engines.join(', ') : 'none (no docker, no podman)')
|
|
509
|
+
.replaceAll('{{PLAN}}', plan.trim())
|
|
510
|
+
.replaceAll('{{PORT}}', String(cfg.port));
|
|
511
|
+
process.stdout.write(out.trimStart());
|
|
512
|
+
return 0;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
/** Run a printing function with stdout swallowed, and give back its return value. */
|
|
516
|
+
async function withSuppressedOutput(fn) {
|
|
517
|
+
const write = process.stdout.write.bind(process.stdout);
|
|
518
|
+
process.stdout.write = () => true;
|
|
519
|
+
try { return await fn(); } finally { process.stdout.write = write; }
|
|
520
|
+
}
|
|
521
|
+
|
|
397
522
|
/**
|
|
398
523
|
* Can this machine stand up a house of its own?
|
|
399
524
|
*
|
|
@@ -1123,7 +1248,13 @@ function cmdUninstall() {
|
|
|
1123
1248
|
break;
|
|
1124
1249
|
}
|
|
1125
1250
|
case 'plugins': process.exitCode = cmdPlugins(); break;
|
|
1126
|
-
case 'prompt':
|
|
1251
|
+
case 'prompt':
|
|
1252
|
+
// Two audiences, two prompts. Bare `prompt` is the memory-USAGE snippet that goes
|
|
1253
|
+
// into a running agent's system prompt; `--install` is the one you hand an agent
|
|
1254
|
+
// that has not installed memhouse yet.
|
|
1255
|
+
if (flags.install === true) process.exitCode = await renderInstallPrompt();
|
|
1256
|
+
else process.stdout.write(fs.readFileSync(path.join(DELIVERY, 'PROMPT.md'), 'utf-8'));
|
|
1257
|
+
break;
|
|
1127
1258
|
case 'reset': process.exitCode = await cmdReset(); break;
|
|
1128
1259
|
case 'deploy': {
|
|
1129
1260
|
const dep = require(path.join(REPO_ROOT, 'mem-house', 'deploy.js'));
|
package/editors/antigravity.js
CHANGED
|
@@ -2,7 +2,16 @@ const { execSync, execFileSync } = require('child_process');
|
|
|
2
2
|
const fs = require('fs');
|
|
3
3
|
const path = require('path');
|
|
4
4
|
const os = require('os');
|
|
5
|
-
|
|
5
|
+
// Required lazily. A top-level require makes this native module a load-time dependency
|
|
6
|
+
// of the whole adapter set: editors/index.js imports every adapter, so one missing
|
|
7
|
+
// binding threw before any adapter ran and `discover` reported ZERO editors — losing the
|
|
8
|
+
// eleven that need no native code at all. The adapterErrors path exists to degrade one
|
|
9
|
+
// adapter at a time, and a top-level throw walks straight past it.
|
|
10
|
+
let Database = null;
|
|
11
|
+
function openDb(...a) {
|
|
12
|
+
if (!Database) Database = require('better-sqlite3');
|
|
13
|
+
return new Database(...a);
|
|
14
|
+
}
|
|
6
15
|
const { getAppDataPath } = require('./base');
|
|
7
16
|
const adapterErrors = require('./adapter-errors');
|
|
8
17
|
|
|
@@ -235,7 +244,7 @@ function readGlobalStateValue(key) {
|
|
|
235
244
|
|
|
236
245
|
let db = null;
|
|
237
246
|
try {
|
|
238
|
-
db =
|
|
247
|
+
db = openDb(ANTIGRAVITY_GLOBAL_STORAGE_DB, { readonly: true, fileMustExist: true });
|
|
239
248
|
const row = db.prepare('SELECT value FROM ItemTable WHERE key = ?').get(key);
|
|
240
249
|
if (!row) return null;
|
|
241
250
|
const v = row.value;
|
package/editors/cursor.js
CHANGED
|
@@ -1,4 +1,13 @@
|
|
|
1
|
-
|
|
1
|
+
// Required lazily. A top-level require makes this native module a load-time dependency
|
|
2
|
+
// of the whole adapter set: editors/index.js imports every adapter, so one missing
|
|
3
|
+
// binding threw before any adapter ran and `discover` reported ZERO editors — losing the
|
|
4
|
+
// eleven that need no native code at all. The adapterErrors path exists to degrade one
|
|
5
|
+
// adapter at a time, and a top-level throw walks straight past it.
|
|
6
|
+
let Database = null;
|
|
7
|
+
function openDb(...a) {
|
|
8
|
+
if (!Database) Database = require('better-sqlite3');
|
|
9
|
+
return new Database(...a);
|
|
10
|
+
}
|
|
2
11
|
const path = require('path');
|
|
3
12
|
const fs = require('fs');
|
|
4
13
|
const os = require('os');
|
|
@@ -147,7 +156,7 @@ function getWorkspaceMap() {
|
|
|
147
156
|
|
|
148
157
|
function getComposerHeaders(stateDbPath) {
|
|
149
158
|
try {
|
|
150
|
-
const db =
|
|
159
|
+
const db = openDb(stateDbPath, { readonly: true });
|
|
151
160
|
const row = db.prepare("SELECT value FROM ItemTable WHERE key = 'composer.composerData'").get();
|
|
152
161
|
db.close();
|
|
153
162
|
if (!row) return [];
|
|
@@ -277,7 +286,7 @@ function getChats() {
|
|
|
277
286
|
// Source 1: ~/.cursor/chats store.db
|
|
278
287
|
for (const { workspace, chatId, dbPath } of getAgentStoreChats()) {
|
|
279
288
|
try {
|
|
280
|
-
const db =
|
|
289
|
+
const db = openDb(dbPath, { readonly: true });
|
|
281
290
|
const meta = readStoreMeta(db);
|
|
282
291
|
db.close();
|
|
283
292
|
if (meta) {
|
|
@@ -316,7 +325,7 @@ function getChats() {
|
|
|
316
325
|
|
|
317
326
|
// Source 2: workspaceStorage composers
|
|
318
327
|
let globalDb = null;
|
|
319
|
-
try { globalDb =
|
|
328
|
+
try { globalDb = openDb(GLOBAL_STORAGE_DB, { readonly: true }); }
|
|
320
329
|
catch (e) {
|
|
321
330
|
// Absent is normal — Cursor may simply not be installed. Unreadable is not.
|
|
322
331
|
if (fs.existsSync(GLOBAL_STORAGE_DB)) adapterErrors.record('cursor', e, GLOBAL_STORAGE_DB);
|
|
@@ -369,7 +378,7 @@ function getChats() {
|
|
|
369
378
|
|
|
370
379
|
function getMessages(chat) {
|
|
371
380
|
if (chat._type === 'agent-store') {
|
|
372
|
-
const db =
|
|
381
|
+
const db = openDb(chat._dbPath, { readonly: true });
|
|
373
382
|
const msgs = collectStoreMessages(db, chat._rootBlobId);
|
|
374
383
|
db.close();
|
|
375
384
|
// Use lastUsedModel as fallback for assistant messages without model info
|
|
@@ -382,7 +391,7 @@ function getMessages(chat) {
|
|
|
382
391
|
}
|
|
383
392
|
|
|
384
393
|
let globalDb;
|
|
385
|
-
try { globalDb =
|
|
394
|
+
try { globalDb = openDb(GLOBAL_STORAGE_DB, { readonly: true }); }
|
|
386
395
|
catch (e) {
|
|
387
396
|
// Returning [] unreported would let a re-ship overwrite this session's stored
|
|
388
397
|
// transcript with nothing.
|
|
@@ -407,7 +416,7 @@ function getMessages(chat) {
|
|
|
407
416
|
|
|
408
417
|
function getCursorAccessToken() {
|
|
409
418
|
try {
|
|
410
|
-
const db =
|
|
419
|
+
const db = openDb(GLOBAL_STORAGE_DB, { readonly: true });
|
|
411
420
|
const row = db.prepare("SELECT value FROM ItemTable WHERE key = 'cursorAuth/accessToken'").get();
|
|
412
421
|
db.close();
|
|
413
422
|
return row ? row.value : null;
|
package/editors/opencode.js
CHANGED
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
const path = require('path');
|
|
2
2
|
const fs = require('fs');
|
|
3
3
|
const os = require('os');
|
|
4
|
-
|
|
4
|
+
// Required lazily. A top-level require makes this native module a load-time dependency
|
|
5
|
+
// of the whole adapter set: editors/index.js imports every adapter, so one missing
|
|
6
|
+
// binding threw before any adapter ran and `discover` reported ZERO editors — losing the
|
|
7
|
+
// eleven that need no native code at all. The adapterErrors path exists to degrade one
|
|
8
|
+
// adapter at a time, and a top-level throw walks straight past it.
|
|
9
|
+
let Database = null;
|
|
10
|
+
function openDb(...a) {
|
|
11
|
+
if (!Database) Database = require('better-sqlite3');
|
|
12
|
+
return new Database(...a);
|
|
13
|
+
}
|
|
5
14
|
const adapterErrors = require('./adapter-errors');
|
|
6
15
|
|
|
7
16
|
// OpenCode stores data in XDG-style paths across all platforms
|
|
@@ -28,7 +37,7 @@ const DB_PATH = getOpenCodeDbPath();
|
|
|
28
37
|
function queryDb(sql) {
|
|
29
38
|
if (!fs.existsSync(DB_PATH)) return [];
|
|
30
39
|
try {
|
|
31
|
-
const db =
|
|
40
|
+
const db = openDb(DB_PATH, { readonly: true });
|
|
32
41
|
const rows = db.prepare(sql).all();
|
|
33
42
|
db.close();
|
|
34
43
|
return rows;
|
|
@@ -85,7 +94,7 @@ function getSqliteSessions() {
|
|
|
85
94
|
function getSqliteMessages(sessionId) {
|
|
86
95
|
if (!fs.existsSync(DB_PATH)) return [];
|
|
87
96
|
try {
|
|
88
|
-
const db =
|
|
97
|
+
const db = openDb(DB_PATH, { readonly: true });
|
|
89
98
|
const messages = db.prepare(
|
|
90
99
|
`SELECT m.id as msg_id, m.data as msg_data, m.time_created
|
|
91
100
|
FROM message m WHERE m.session_id = ? ORDER BY m.time_created ASC`
|
package/editors/zed.js
CHANGED
|
@@ -2,7 +2,16 @@ const path = require('path');
|
|
|
2
2
|
const fs = require('fs');
|
|
3
3
|
const os = require('os');
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
// Required lazily. A top-level require makes this native module a load-time dependency
|
|
6
|
+
// of the whole adapter set: editors/index.js imports every adapter, so one missing
|
|
7
|
+
// binding threw before any adapter ran and `discover` reported ZERO editors — losing the
|
|
8
|
+
// eleven that need no native code at all. The adapterErrors path exists to degrade one
|
|
9
|
+
// adapter at a time, and a top-level throw walks straight past it.
|
|
10
|
+
let Database = null;
|
|
11
|
+
function openDb(...a) {
|
|
12
|
+
if (!Database) Database = require('better-sqlite3');
|
|
13
|
+
return new Database(...a);
|
|
14
|
+
}
|
|
6
15
|
const adapterErrors = require('./adapter-errors');
|
|
7
16
|
|
|
8
17
|
// Zed stores data in different locations depending on the platform
|
|
@@ -69,7 +78,7 @@ function decompressZstd(buf) {
|
|
|
69
78
|
function queryDb(sql) {
|
|
70
79
|
if (!fs.existsSync(THREADS_DB)) return [];
|
|
71
80
|
try {
|
|
72
|
-
const db =
|
|
81
|
+
const db = openDb(THREADS_DB, { readonly: true });
|
|
73
82
|
const rows = db.prepare(sql).all();
|
|
74
83
|
db.close();
|
|
75
84
|
return rows;
|
|
@@ -91,7 +100,7 @@ function getThreadColumns() {
|
|
|
91
100
|
if (threadColumns) return threadColumns;
|
|
92
101
|
if (!fs.existsSync(THREADS_DB)) return new Set();
|
|
93
102
|
try {
|
|
94
|
-
const db =
|
|
103
|
+
const db = openDb(THREADS_DB, { readonly: true });
|
|
95
104
|
const cols = db.prepare('PRAGMA table_info(threads)').all().map((r) => r.name);
|
|
96
105
|
db.close();
|
|
97
106
|
// PRAGMA on a missing or renamed table SUCCEEDS with zero rows rather than
|
|
@@ -122,7 +131,7 @@ function resetCache() {
|
|
|
122
131
|
function queryBlob(id) {
|
|
123
132
|
if (!fs.existsSync(THREADS_DB)) return null;
|
|
124
133
|
try {
|
|
125
|
-
const db =
|
|
134
|
+
const db = openDb(THREADS_DB, { readonly: true });
|
|
126
135
|
const row = db.prepare('SELECT data FROM threads WHERE id = ?').get(id);
|
|
127
136
|
db.close();
|
|
128
137
|
return row ? row.data : null;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# memhouse — agent install prompt (template)
|
|
2
|
+
|
|
3
|
+
`memhouse prompt --install` renders this with THIS machine's facts substituted and
|
|
4
|
+
prints it. Feed the output to a coding agent; it has everything needed to install and
|
|
5
|
+
onboard without asking the user to look anything up.
|
|
6
|
+
|
|
7
|
+
The point of rendering rather than shipping a static runbook: the CLI has already
|
|
8
|
+
probed the machine, so the prompt states the route that applies instead of making the
|
|
9
|
+
agent reason through a decision tree and possibly pick wrong. `AGENT-INSTALL.md` is the
|
|
10
|
+
static long-form version, and it predates `deploy --local`.
|
|
11
|
+
|
|
12
|
+
Placeholders are `{{NAME}}`. Everything below the marker is the prompt body.
|
|
13
|
+
|
|
14
|
+
<!-- PROMPT BODY BELOW -->
|
|
15
|
+
You are installing **memhouse** for the user on this machine.
|
|
16
|
+
|
|
17
|
+
memhouse gives a coding agent long-term memory of past sessions. It reads the session
|
|
18
|
+
transcripts your editors already write to disk — 17 of them — parses them locally, and
|
|
19
|
+
ships typed rows into ClickHouse. Nothing is intercepted or proxied; it reads files that
|
|
20
|
+
already exist.
|
|
21
|
+
|
|
22
|
+
## What this machine looks like right now
|
|
23
|
+
|
|
24
|
+
- memhouse version: **{{VERSION}}**
|
|
25
|
+
- platform: {{PLATFORM}}, node {{NODE}}
|
|
26
|
+
- current state: **{{STATE}}**
|
|
27
|
+
- editors with sessions found: {{EDITORS}}
|
|
28
|
+
- reachable ClickHouse: {{HOUSE}}
|
|
29
|
+
- container engines available: {{ENGINES}}
|
|
30
|
+
|
|
31
|
+
## Your task
|
|
32
|
+
|
|
33
|
+
{{PLAN}}
|
|
34
|
+
|
|
35
|
+
## Rules that matter
|
|
36
|
+
|
|
37
|
+
- **Never invent a credential.** If a step needs a password for a house the user already
|
|
38
|
+
runs, ask them for it. Do not guess, do not read it out of a file they did not name.
|
|
39
|
+
- **Do not run `deploy --down`.** It destroys the container *and its volume*. It is never
|
|
40
|
+
part of an install.
|
|
41
|
+
- The install writes `~/.memhouse/env` at mode 0600. Do not print its contents; do not
|
|
42
|
+
echo a password into the transcript you are writing, which memhouse will then ship.
|
|
43
|
+
- If a command fails, read what it printed before retrying. memhouse's failures name the
|
|
44
|
+
cause and usually name the fix — it is not a generic tool that fails generically.
|
|
45
|
+
- Rooms are named for the ClickHouse user: `sessions_<them>`, `messages_<them>`,
|
|
46
|
+
`tool_calls_<them>`. There are no unsuffixed tables. If you write a query, suffix it.
|
|
47
|
+
|
|
48
|
+
## When you are done
|
|
49
|
+
|
|
50
|
+
Run `memhouse doctor` and report its output to the user. Every line should be a check
|
|
51
|
+
mark. Then tell them:
|
|
52
|
+
|
|
53
|
+
- where the dashboard is (`http://localhost:{{PORT}}`) and that `memhouse start` runs it
|
|
54
|
+
- that `memhouse search <terms>` searches every past session from every editor
|
|
55
|
+
- that you can now search their history yourself, and offer to try one query
|
|
56
|
+
|
|
57
|
+
If `doctor` reports a failure, say which line failed and what memhouse suggested. Do not
|
|
58
|
+
declare success on a partial install.
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
# mem-house — agent install
|
|
2
2
|
|
|
3
3
|
Imperative steps for an AI coding agent installing mem-house on a machine. Execute
|
|
4
|
-
in order; every command is copy-pasteable.
|
|
4
|
+
in order; every command is copy-pasteable.
|
|
5
|
+
|
|
6
|
+
**Prefer `memhouse prompt --install`.** It renders `AGENT-INSTALL-PROMPT.md` against the
|
|
7
|
+
machine it runs on — which editors have sessions, whether a ClickHouse answers, whether
|
|
8
|
+
docker or podman exists — and states the one route that applies. This file is the static
|
|
9
|
+
long-form version and assumes a reachable ClickHouse already exists, which is the case
|
|
10
|
+
`deploy --local` was added to remove. Prereqs: Node >= 20.19 and a reachable
|
|
5
11
|
ClickHouse >= 26.2 (local server, Cloud, or a kernel house — see
|
|
6
12
|
`kernel-install.md` for the kernel path). No checkout required — memhouse is on npm.
|
|
7
13
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "memhouse",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.3",
|
|
4
4
|
"description": "memhouse — agent conversation memory across 17 editors: parse-on-client shippers, a typed ClickHouse store with one set of rooms per member, kernel-agency install, and the agentlytics dashboard",
|
|
5
5
|
"main": "bin/memhouse.js",
|
|
6
6
|
"bin": {
|