@xqli02/mneme 0.1.5 → 0.1.6

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/mneme.mjs CHANGED
@@ -4,7 +4,7 @@
4
4
  * mneme CLI — Three-layer memory architecture for AI coding agents.
5
5
  *
6
6
  * Unified entry point that routes to:
7
- * 1. mneme's own commands (init, doctor, status, compact, facts, propose, review, auto)
7
+ * 1. mneme's own commands (init, doctor, status, compact, facts, propose, review, auto, server)
8
8
  * 2. opencode commands (run, web, serve, etc.) — default fallback
9
9
  * 3. bd/beads commands (ready, list, create, close, etc.)
10
10
  *
@@ -56,6 +56,7 @@ const MNEME_COMMANDS = new Set([
56
56
  "propose",
57
57
  "review",
58
58
  "auto",
59
+ "server",
59
60
  "version",
60
61
  "--version",
61
62
  "-v",
@@ -118,6 +119,11 @@ switch (command) {
118
119
  await auto(args.slice(1));
119
120
  break;
120
121
  }
122
+ case "server": {
123
+ const { server } = await import("../src/commands/server.mjs");
124
+ await server(args.slice(1));
125
+ break;
126
+ }
121
127
  case "version":
122
128
  case "--version":
123
129
  case "-v":
@@ -146,6 +152,12 @@ Usage:
146
152
  mneme status Show three-layer memory dashboard
147
153
  mneme compact Pre-compaction persistence check
148
154
 
155
+ ${bold("Dolt server:")}
156
+ mneme server start Start the dolt server
157
+ mneme server stop Stop the dolt server
158
+ mneme server status Show server status (port, PID, data-dir)
159
+ mneme server restart Restart the dolt server
160
+
149
161
  ${bold("Task management (beads):")}
150
162
  mneme ready Show tasks with no blockers
151
163
  mneme list [--status=STATUS] List tasks
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xqli02/mneme",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "description": "Three-layer memory architecture for AI coding agents (Ledger + Beads + OpenCode)",
5
5
  "type": "module",
6
6
  "bin": {
@@ -236,47 +236,23 @@ function installBd() {
236
236
 
237
237
  // ── Dolt server + bd init ───────────────────────────────────────────────────
238
238
 
239
- /**
240
- * Shared dolt data directory. All projects store their databases here,
241
- * isolated by database name (e.g. beads_projectA, beads_projectB).
242
- * One dolt server on port 3307 serves all projects on this machine.
243
- */
244
- const DOLT_DATA_DIR = process.env.MNEME_DOLT_DATA_DIR
245
- || join(process.env.HOME, ".dolt", "databases");
246
-
247
- const DOLT_PORT = parseInt(process.env.MNEME_DOLT_PORT || "3307", 10);
239
+ import { DOLT_DATA_DIR, DOLT_PORT, isPortOpen, findDoltProcess, startDoltServer, killDoltProcess } from "../dolt.mjs";
248
240
 
249
241
  function ensureDoltServer() {
250
- if (!existsSync(DOLT_DATA_DIR)) {
251
- mkdirSync(DOLT_DATA_DIR, { recursive: true });
252
- }
253
-
254
- // Check if port is already in use (bash /dev/tcp returns 0 if open, 1 if refused)
255
- const portInUse = run(`bash -c 'echo > /dev/tcp/127.0.0.1/${DOLT_PORT}' 2>&1`) !== null;
256
-
257
- if (portInUse) {
258
- // Port is occupied — check if it's our dolt server with the right data-dir
259
- const psOutput = run(`ps aux 2>/dev/null`) ?? "";
260
- const doltLines = psOutput.split("\n").filter((line) =>
261
- line.includes("dolt") && line.includes("sql-server") && line.includes(`${DOLT_PORT}`)
262
- && !line.includes("grep")
263
- );
264
- // Prefer the actual dolt binary process over a bash wrapper
265
- const doltProc = doltLines.find((l) => !l.includes("bash -c") && /\bdolt\s+sql-server\b/.test(l)) || doltLines[0];
242
+ // Check if port is already in use
243
+ if (isPortOpen()) {
244
+ const info = findDoltProcess();
266
245
 
267
- if (doltProc && doltProc.includes(DOLT_DATA_DIR)) {
246
+ if (info && info.dataDir === DOLT_DATA_DIR) {
268
247
  // Same data-dir, already running — nothing to do
269
248
  log.ok(`dolt server already running ${color.dim(`(port ${DOLT_PORT}, data-dir ${DOLT_DATA_DIR})`)}`);
270
249
  return true;
271
250
  }
272
251
 
273
- if (doltProc) {
252
+ if (info) {
274
253
  // Dolt running but with a different data-dir — kill and restart
275
254
  log.warn(`dolt server on port ${DOLT_PORT} uses a different data-dir, restarting...`);
276
- // Extract PID from ps aux output (second column)
277
- const pid = doltProc.trim().split(/\s+/)[1];
278
- if (pid) run(`kill ${pid} 2>/dev/null`);
279
- run("sleep 1");
255
+ killDoltProcess();
280
256
  } else {
281
257
  // Port occupied by something else entirely
282
258
  log.fail(`Port ${DOLT_PORT} is in use by a non-dolt process. Set MNEME_DOLT_PORT to use a different port.`);
@@ -285,24 +261,12 @@ function ensureDoltServer() {
285
261
  }
286
262
 
287
263
  log.info(`Starting dolt server (port ${DOLT_PORT}, data-dir ${DOLT_DATA_DIR})...`);
288
- const logFile = join(DOLT_DATA_DIR, "server.log");
289
-
290
- // Start in background — shared data dir, all project databases coexist
291
- run(
292
- `nohup dolt sql-server --host 127.0.0.1 --port ${DOLT_PORT} --data-dir "${DOLT_DATA_DIR}" > "${logFile}" 2>&1 &`,
293
- );
294
-
295
- // Wait for server to be ready (up to 10s)
296
- for (let i = 0; i < 10; i++) {
297
- run("sleep 1");
298
- const alive = run(`bash -c 'echo > /dev/tcp/127.0.0.1/${DOLT_PORT}' 2>&1`) !== null;
299
- if (alive) {
300
- log.ok(`dolt server started ${color.dim(`(port ${DOLT_PORT})`)}`);
301
- return true;
302
- }
264
+ if (startDoltServer()) {
265
+ log.ok(`dolt server started ${color.dim(`(port ${DOLT_PORT})`)}`);
266
+ return true;
303
267
  }
304
268
 
305
- log.fail(`dolt server failed to start. Check ${logFile}`);
269
+ log.fail(`dolt server failed to start. Check ${DOLT_DATA_DIR}/server.log`);
306
270
  return false;
307
271
  }
308
272
 
@@ -0,0 +1,144 @@
1
+ /**
2
+ * mneme server — Manage the dolt SQL server.
3
+ *
4
+ * Subcommands:
5
+ * mneme server start Start the dolt server
6
+ * mneme server stop Stop the dolt server
7
+ * mneme server status Show server status
8
+ * mneme server restart Restart the dolt server
9
+ */
10
+
11
+ import { DOLT_DATA_DIR, DOLT_PORT, isPortOpen, findDoltProcess, startDoltServer, killDoltProcess } from "../dolt.mjs";
12
+ import { has, log, color } from "../utils.mjs";
13
+
14
+ function showStatus() {
15
+ if (!has("dolt")) {
16
+ log.fail("dolt is not installed");
17
+ return false;
18
+ }
19
+
20
+ const open = isPortOpen();
21
+ if (!open) {
22
+ log.info(`dolt server is ${color.red("stopped")} ${color.dim(`(port ${DOLT_PORT})`)}`);
23
+ return false;
24
+ }
25
+
26
+ const info = findDoltProcess();
27
+ if (info) {
28
+ const dirMatch = info.dataDir === DOLT_DATA_DIR;
29
+ const dirStatus = dirMatch
30
+ ? color.green(info.dataDir)
31
+ : `${color.red(info.dataDir)} (expected: ${DOLT_DATA_DIR})`;
32
+
33
+ console.log(`
34
+ ${color.bold("dolt server")} — ${color.green("running")}
35
+ Port: ${DOLT_PORT}
36
+ PID: ${info.pid}
37
+ Data dir: ${dirStatus}
38
+ `);
39
+
40
+ if (!dirMatch) {
41
+ log.warn("Data dir mismatch. Run 'mneme server restart' to fix.");
42
+ }
43
+ } else {
44
+ log.info(`Port ${DOLT_PORT} is open but no dolt process found — may be a different service.`);
45
+ }
46
+
47
+ return true;
48
+ }
49
+
50
+ function doStart() {
51
+ if (!has("dolt")) {
52
+ log.fail("dolt is not installed. Run 'mneme init' or install manually.");
53
+ process.exit(1);
54
+ }
55
+
56
+ if (isPortOpen()) {
57
+ const info = findDoltProcess();
58
+ if (info && info.dataDir === DOLT_DATA_DIR) {
59
+ log.ok(`dolt server already running ${color.dim(`(port ${DOLT_PORT}, PID ${info.pid})`)}`);
60
+ return;
61
+ }
62
+ if (info) {
63
+ log.warn(`dolt server on port ${DOLT_PORT} uses different data-dir (${info.dataDir}), restarting...`);
64
+ killDoltProcess();
65
+ } else {
66
+ log.fail(`Port ${DOLT_PORT} is in use by a non-dolt process. Set MNEME_DOLT_PORT to use a different port.`);
67
+ process.exit(1);
68
+ }
69
+ }
70
+
71
+ log.info(`Starting dolt server (port ${DOLT_PORT}, data-dir ${DOLT_DATA_DIR})...`);
72
+ if (startDoltServer()) {
73
+ log.ok(`dolt server started ${color.dim(`(port ${DOLT_PORT})`)}`);
74
+ } else {
75
+ log.fail(`Failed to start dolt server. Check ${DOLT_DATA_DIR}/server.log`);
76
+ process.exit(1);
77
+ }
78
+ }
79
+
80
+ function doStop() {
81
+ if (!isPortOpen()) {
82
+ log.info("dolt server is not running.");
83
+ return;
84
+ }
85
+
86
+ const info = findDoltProcess();
87
+ if (!info) {
88
+ log.warn(`Port ${DOLT_PORT} is open but no dolt process found. Cannot stop.`);
89
+ return;
90
+ }
91
+
92
+ log.info(`Stopping dolt server (PID ${info.pid})...`);
93
+ if (killDoltProcess()) {
94
+ log.ok("dolt server stopped.");
95
+ } else {
96
+ log.fail("Failed to stop dolt server. Try: kill " + info.pid);
97
+ }
98
+ }
99
+
100
+ function doRestart() {
101
+ if (isPortOpen()) {
102
+ const info = findDoltProcess();
103
+ if (info) {
104
+ log.info(`Stopping dolt server (PID ${info.pid})...`);
105
+ killDoltProcess();
106
+ }
107
+ }
108
+
109
+ doStart();
110
+ }
111
+
112
+ export async function server(args = []) {
113
+ const sub = args[0];
114
+
115
+ switch (sub) {
116
+ case "start":
117
+ doStart();
118
+ break;
119
+ case "stop":
120
+ doStop();
121
+ break;
122
+ case "status":
123
+ showStatus();
124
+ break;
125
+ case "restart":
126
+ doRestart();
127
+ break;
128
+ default:
129
+ console.log(`
130
+ ${color.bold("mneme server")} — Manage the dolt SQL server
131
+
132
+ Usage:
133
+ mneme server start Start the dolt server
134
+ mneme server stop Stop the dolt server
135
+ mneme server status Show server status
136
+ mneme server restart Restart the dolt server
137
+
138
+ Environment:
139
+ MNEME_DOLT_DATA_DIR Data directory (default: ~/.dolt/databases)
140
+ MNEME_DOLT_PORT Port (default: 3307)
141
+ `);
142
+ break;
143
+ }
144
+ }
package/src/dolt.mjs ADDED
@@ -0,0 +1,108 @@
1
+ /**
2
+ * Shared dolt server utilities.
3
+ *
4
+ * Used by both `mneme init` and `mneme server`.
5
+ */
6
+
7
+ import { existsSync, mkdirSync } from "node:fs";
8
+ import { join } from "node:path";
9
+ import { run } from "./utils.mjs";
10
+
11
+ /**
12
+ * Shared dolt data directory. All projects store their databases here,
13
+ * isolated by database name (e.g. beads_projectA, beads_projectB).
14
+ * One dolt server on port 3307 serves all projects on this machine.
15
+ */
16
+ export const DOLT_DATA_DIR = process.env.MNEME_DOLT_DATA_DIR
17
+ || join(process.env.HOME, ".dolt", "databases");
18
+
19
+ export const DOLT_PORT = parseInt(process.env.MNEME_DOLT_PORT || "3307", 10);
20
+
21
+ /**
22
+ * Check if a TCP port is accepting connections.
23
+ */
24
+ export function isPortOpen(port = DOLT_PORT) {
25
+ return run(`bash -c 'echo > /dev/tcp/127.0.0.1/${port}' 2>&1`) !== null;
26
+ }
27
+
28
+ /**
29
+ * Find dolt sql-server process(es) on the given port.
30
+ * Returns { proc, pid, dataDir } or null if not found.
31
+ *
32
+ * Prefers the actual dolt binary process over bash wrappers.
33
+ */
34
+ export function findDoltProcess(port = DOLT_PORT) {
35
+ const psOutput = run(`ps aux 2>/dev/null`) ?? "";
36
+ const doltLines = psOutput.split("\n").filter((line) =>
37
+ line.includes("dolt") && line.includes("sql-server") && line.includes(`${port}`)
38
+ && !line.includes("grep")
39
+ );
40
+
41
+ if (doltLines.length === 0) return null;
42
+
43
+ // Prefer the actual dolt binary process over a bash wrapper
44
+ const proc = doltLines.find((l) => !l.includes("bash -c") && /\bdolt\s+sql-server\b/.test(l))
45
+ || doltLines[0];
46
+
47
+ const pid = proc.trim().split(/\s+/)[1];
48
+
49
+ // Extract --data-dir value from the process command line
50
+ const dataDirMatch = proc.match(/--data-dir\s+(\S+)/);
51
+ const dataDir = dataDirMatch ? dataDirMatch[1] : null;
52
+
53
+ return { proc, pid, dataDir, allLines: doltLines };
54
+ }
55
+
56
+ /**
57
+ * Ensure the dolt data directory exists.
58
+ */
59
+ export function ensureDataDir() {
60
+ if (!existsSync(DOLT_DATA_DIR)) {
61
+ mkdirSync(DOLT_DATA_DIR, { recursive: true });
62
+ }
63
+ }
64
+
65
+ /**
66
+ * Start the dolt server in the background. Returns true if started successfully.
67
+ * Does NOT check if already running — caller should check first.
68
+ */
69
+ export function startDoltServer() {
70
+ ensureDataDir();
71
+
72
+ const logFile = join(DOLT_DATA_DIR, "server.log");
73
+
74
+ run(
75
+ `nohup dolt sql-server --host 127.0.0.1 --port ${DOLT_PORT} --data-dir "${DOLT_DATA_DIR}" > "${logFile}" 2>&1 &`,
76
+ );
77
+
78
+ // Wait for server to be ready (up to 10s)
79
+ for (let i = 0; i < 10; i++) {
80
+ run("sleep 1");
81
+ if (isPortOpen()) {
82
+ return true;
83
+ }
84
+ }
85
+
86
+ return false;
87
+ }
88
+
89
+ /**
90
+ * Kill a dolt process by PID. Also attempts to kill related wrapper processes.
91
+ */
92
+ export function killDoltProcess(port = DOLT_PORT) {
93
+ const info = findDoltProcess(port);
94
+ if (!info) return false;
95
+
96
+ // Kill all matching dolt lines (binary + wrapper)
97
+ const pids = info.allLines
98
+ .map((l) => l.trim().split(/\s+/)[1])
99
+ .filter(Boolean);
100
+
101
+ for (const pid of pids) {
102
+ run(`kill ${pid} 2>/dev/null`);
103
+ }
104
+
105
+ // Wait briefly for process to exit
106
+ run("sleep 1");
107
+ return !isPortOpen(port);
108
+ }