omo-memory 0.1.12 → 0.1.13

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 CHANGED
@@ -39,6 +39,7 @@ After the package is published to npm, use the same package for CLI and MCP:
39
39
 
40
40
  ```sh
41
41
  npx -y omo-memory init
42
+ npx -y omo-memory update
42
43
  npx -y omo-memory global scan --root .
43
44
  npx -y omo-memory global migrate --root . --global-db ~/.omo/memory/global.sqlite
44
45
  npx -y omo-memory session bootstrap --host codex --adapter lazycodex --limit 5
@@ -140,6 +141,22 @@ Initial stdio MCP tools:
140
141
  - `memory_ontology_supersede`
141
142
  - `memory_ontology_recall`
142
143
 
144
+ ## Updates
145
+
146
+ Installed CLI commands automatically launch a quiet background `npm install -g omo-memory@latest` at most once per day. MCP startup does not run the updater, so stdio handshakes stay clean.
147
+
148
+ Manual update:
149
+
150
+ ```sh
151
+ omo-memory update
152
+ ```
153
+
154
+ Disable automatic update for pinned environments:
155
+
156
+ ```sh
157
+ OMO_MEMORY_AUTO_UPDATE=0 omo-memory doctor
158
+ ```
159
+
143
160
  ## Second-brain layer
144
161
 
145
162
  The base ledger remains project-local and chronological: sessions, events, handoffs, and explicit recall. The second-brain layer adds deterministic ontology tables and lifecycle commands:
@@ -0,0 +1,84 @@
1
+ import { spawn, spawnSync } from "node:child_process";
2
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
3
+ import { homedir } from "node:os";
4
+ import { dirname, join } from "node:path";
5
+ const PACKAGE_NAME = "omo-memory";
6
+ const DEFAULT_INTERVAL_MS = 86_400_000;
7
+ const STATE_PATH = join(homedir(), ".omo", "memory", "auto-update.json");
8
+ export function maybeRunAutoUpdate(currentVersion, nowMs = Date.now()) {
9
+ const statePath = process.env["OMO_MEMORY_UPDATE_STATE"] ?? STATE_PATH;
10
+ if (!shouldAttemptAutoUpdate({ nowMs, statePath }))
11
+ return;
12
+ writeAttemptStamp(statePath, nowMs);
13
+ const child = spawn(npmCommand(), installArgs(), {
14
+ detached: true,
15
+ stdio: "ignore",
16
+ env: updateEnv(currentVersion),
17
+ });
18
+ child.unref();
19
+ }
20
+ export function runAutoUpdate(currentVersion) {
21
+ const command = npmCommand();
22
+ const args = installArgs();
23
+ const result = spawnSync(command, args, { encoding: "utf8", env: updateEnv(currentVersion) });
24
+ return {
25
+ ok: result.status === 0,
26
+ packageName: PACKAGE_NAME,
27
+ currentVersion,
28
+ command: [command, ...args],
29
+ status: result.status,
30
+ stdout: result.stdout,
31
+ stderr: result.stderr,
32
+ };
33
+ }
34
+ function shouldAttemptAutoUpdate(input) {
35
+ if (process.env["OMO_MEMORY_AUTO_UPDATE"] === "0")
36
+ return false;
37
+ if (process.env["OMO_MEMORY_AUTO_UPDATE"] === "false")
38
+ return false;
39
+ if (process.env["OMO_MEMORY_AUTO_UPDATE_CHILD"] === "1")
40
+ return false;
41
+ const intervalMs = updateIntervalMs();
42
+ if (!existsSync(input.statePath))
43
+ return true;
44
+ const lastAttemptMs = readLastAttemptMs(input.statePath);
45
+ return lastAttemptMs === null || input.nowMs - lastAttemptMs >= intervalMs;
46
+ }
47
+ function updateIntervalMs() {
48
+ const raw = process.env["OMO_MEMORY_AUTO_UPDATE_INTERVAL_MS"];
49
+ if (raw === undefined)
50
+ return DEFAULT_INTERVAL_MS;
51
+ const parsed = Number(raw);
52
+ return Number.isFinite(parsed) && parsed >= 0 ? parsed : DEFAULT_INTERVAL_MS;
53
+ }
54
+ function readLastAttemptMs(statePath) {
55
+ try {
56
+ const parsed = JSON.parse(readFileSync(statePath, "utf8"));
57
+ if (!isRecord(parsed))
58
+ return null;
59
+ const value = parsed["lastAttemptMs"];
60
+ return typeof value === "number" && Number.isFinite(value) ? value : null;
61
+ }
62
+ catch (error) {
63
+ if (error instanceof Error)
64
+ return null;
65
+ throw error;
66
+ }
67
+ }
68
+ function writeAttemptStamp(statePath, nowMs) {
69
+ mkdirSync(dirname(statePath), { recursive: true });
70
+ writeFileSync(statePath, `${JSON.stringify({ lastAttemptMs: nowMs })}\n`);
71
+ }
72
+ function npmCommand() {
73
+ return process.env["OMO_MEMORY_NPM_COMMAND"] ?? "npm";
74
+ }
75
+ function installArgs() {
76
+ const target = process.env["OMO_MEMORY_UPDATE_TARGET"] ?? `${PACKAGE_NAME}@latest`;
77
+ return ["install", "-g", target];
78
+ }
79
+ function updateEnv(currentVersion) {
80
+ return { ...process.env, OMO_MEMORY_AUTO_UPDATE_CHILD: "1", OMO_MEMORY_CURRENT_VERSION: currentVersion };
81
+ }
82
+ function isRecord(value) {
83
+ return value !== null && typeof value === "object";
84
+ }
package/dist/cli.js CHANGED
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { readFileSync } from "node:fs";
3
+ import { maybeRunAutoUpdate, runAutoUpdate } from "./autoUpdate.js";
3
4
  import { applyConceptExtraction } from "./conceptExtraction.js";
4
5
  import { migrateToGlobalMemory, scanForMemoryDbs } from "./globalMemory.js";
5
6
  import { runGraphTui } from "./graphTui.js";
@@ -21,6 +22,12 @@ async function main(argv) {
21
22
  await runMcpServer();
22
23
  return;
23
24
  }
25
+ const currentVersion = readPackageVersion();
26
+ if (command === "update") {
27
+ process.stdout.write(`${JSON.stringify(runAutoUpdate(currentVersion), null, 2)}\n`);
28
+ return;
29
+ }
30
+ maybeRunAutoUpdate(currentVersion);
24
31
  if (command === "graph" && subcommand === "tui") {
25
32
  const query = readFlag(rest, "--query");
26
33
  await runGraphTui({ dbPath: readFlag(rest, "--db") ?? defaultDbPath(), ...(query === undefined ? {} : { query }) });
@@ -200,7 +207,17 @@ function fail(message) {
200
207
  throw new Error(message);
201
208
  }
202
209
  function printHelp() {
203
- process.stdout.write(`OMO Memory\n\nCommands:\n omo-memory init\n omo-memory doctor\n omo-memory export\n omo-memory purge --yes\n omo-memory global scan --root <path> [--json]\n omo-memory global migrate --root <path> --global-db <path> [--json]\n omo-memory ontology candidates\n omo-memory ontology score\n omo-memory ontology promote --concept <label|id> [--summary <text>] [--body <text>]\n omo-memory ontology demote --id <durable-id>\n omo-memory ontology supersede --id <durable-id> [--summary <text>]\n omo-memory ontology recall --query <text> [--limit <n>]\n omo-memory session start --host <codex|opencode|grok|unknown> --adapter <name>\n omo-memory session bootstrap --host <codex|opencode|grok|unknown> --adapter <name> [--limit <n>]\n omo-memory event record --type <type> --summary <text> [--session-id <id>]\n omo-memory recent [--limit <n>]\n omo-memory recall --query <text> [--limit <n>]\n omo-memory handoff write (--summary <text> | --summary-file <path>) [--session-id <id>]\n omo-memory graph tui [--db <path>] [--query <text>] (requires Bun on PATH)\n omo-memory mcp\n`);
210
+ process.stdout.write(`OMO Memory\n\nCommands:\n omo-memory init\n omo-memory doctor\n omo-memory update\n omo-memory export\n omo-memory purge --yes\n omo-memory global scan --root <path> [--json]\n omo-memory global migrate --root <path> --global-db <path> [--json]\n omo-memory ontology candidates\n omo-memory ontology score\n omo-memory ontology promote --concept <label|id> [--summary <text>] [--body <text>]\n omo-memory ontology demote --id <durable-id>\n omo-memory ontology supersede --id <durable-id> [--summary <text>]\n omo-memory ontology recall --query <text> [--limit <n>]\n omo-memory session start --host <codex|opencode|grok|unknown> --adapter <name>\n omo-memory session bootstrap --host <codex|opencode|grok|unknown> --adapter <name> [--limit <n>]\n omo-memory event record --type <type> --summary <text> [--session-id <id>]\n omo-memory recent [--limit <n>]\n omo-memory recall --query <text> [--limit <n>]\n omo-memory handoff write (--summary <text> | --summary-file <path>) [--session-id <id>]\n omo-memory graph tui [--db <path>] [--query <text>] (requires Bun on PATH)\n omo-memory mcp\n`);
211
+ }
212
+ function readPackageVersion() {
213
+ const rawPackage = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
214
+ if (!isObject(rawPackage))
215
+ return "0.0.0";
216
+ const version = rawPackage["version"];
217
+ return typeof version === "string" && version.length > 0 ? version : "0.0.0";
218
+ }
219
+ function isObject(value) {
220
+ return value !== null && typeof value === "object";
204
221
  }
205
222
  main(process.argv.slice(2)).catch((error) => {
206
223
  const message = error instanceof Error ? error.message : String(error);
@@ -150,6 +150,8 @@ Use these tools:
150
150
  - `memory_ontology_supersede`
151
151
  - `memory_ontology_recall`
152
152
 
153
+ CLI updates: normal CLI commands automatically launch a quiet background `npm install -g omo-memory@latest` at most once per day. MCP startup intentionally does not auto-update because stdout/stderr must remain reserved for the protocol. Hosts that need pinned installs should set `OMO_MEMORY_AUTO_UPDATE=0` in the CLI environment.
154
+
153
155
  Global migration is copy/import only. Adapters may scan for existing project-local `.omo/memory/state.sqlite` databases and import them into a user-selected global SQLite file, but they must preserve source DBs and retain source provenance in the global store.
154
156
 
155
157
  Global migration also materializes an aggregate OMO schema view inside the global SQLite file. This lets existing ontology extraction, retention scoring, recall, and OpenTUI graph code operate on integrated cross-project events while `global_*` tables retain source database provenance.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "omo-memory",
3
- "version": "0.1.12",
3
+ "version": "0.1.13",
4
4
  "description": "Host-neutral local SQLite memory and session ledger for OMO adapters, exposed through CLI and MCP.",
5
5
  "type": "module",
6
6
  "files": [