omo-memory 0.1.1 → 0.1.2

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
@@ -32,6 +32,7 @@ After the package is published to npm, use the same package for CLI and MCP:
32
32
 
33
33
  ```sh
34
34
  npx -y omo-memory init
35
+ npx -y omo-memory hooks install --host all
35
36
  npx -y omo-memory session bootstrap --host codex --adapter lazycodex --limit 5
36
37
  npx -y omo-memory recent --limit 5
37
38
  npx -y omo-memory mcp
@@ -64,6 +65,21 @@ grok mcp add omo-memory -- npx -y omo-memory mcp
64
65
 
65
66
  Both hosts use `~/.omo/memory/state.sqlite` by default. The `host` value is recorded when an adapter calls `memory_start_session`, not by installing separate servers.
66
67
 
68
+ ## Passive setup
69
+
70
+ Install passive skills/rules/hooks with:
71
+
72
+ ```sh
73
+ npx -y omo-memory hooks install --host all
74
+ ```
75
+
76
+ This installs:
77
+
78
+ - Codex: `~/.codex/skills/omo-memory/SKILL.md` plus a global `~/.codex/AGENTS.md` lifecycle rule.
79
+ - Grok: `~/.grok/skills/omo-memory/SKILL.md`, a global `~/.grok/AGENTS.md` lifecycle rule, and a `~/.grok/hooks/` SessionStart hook.
80
+
81
+ Codex currently exposes MCP and global instruction surfaces, but not a standalone user hook add command like Grok. The Codex passive behavior is therefore rule/skill-driven; Grok additionally gets an executable SessionStart hook.
82
+
67
83
  ## Session bootstrap
68
84
 
69
85
  Adapters should call the bootstrap tool at the beginning of each host session:
package/dist/cli.js CHANGED
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { readFileSync } from "node:fs";
3
+ import { installHooks } from "./hooks.js";
3
4
  import { bootstrapSession, doctorReport, exportMemory, initMemory, purgeMemory, recentEvents, recordEvent, startSession, writeHandoff } from "./memory.js";
4
5
  import { runMcpServer } from "./mcp.js";
5
6
  async function main(argv) {
@@ -29,6 +30,9 @@ function runCommand(command, subcommand, rest) {
29
30
  const args = [subcommand, ...rest].filter((value) => value !== undefined);
30
31
  return { ok: true, ...purgeMemory({ yes: args.includes("--yes") }) };
31
32
  }
33
+ if (command === "hooks" && subcommand === "install") {
34
+ return { ok: true, ...installHooks({ host: parseHookInstallHost(readFlag(rest, "--host") ?? "all") }) };
35
+ }
32
36
  if (command === "session" && subcommand === "start") {
33
37
  const host = parseHost(readFlag(rest, "--host") ?? "unknown");
34
38
  const adapter = readFlag(rest, "--adapter") ?? "unknown";
@@ -73,6 +77,11 @@ function parseHost(value) {
73
77
  return value;
74
78
  fail("--host must be one of codex, opencode, grok, unknown");
75
79
  }
80
+ function parseHookInstallHost(value) {
81
+ if (value === "codex" || value === "grok" || value === "all")
82
+ return value;
83
+ fail("--host must be one of codex, grok, all");
84
+ }
76
85
  function readPositiveIntFlag(args, name, defaultValue) {
77
86
  return parsePositiveInt(readFlag(args, name) ?? String(defaultValue), name);
78
87
  }
@@ -86,7 +95,7 @@ function fail(message) {
86
95
  throw new Error(message);
87
96
  }
88
97
  function printHelp() {
89
- 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 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 handoff write (--summary <text> | --summary-file <path>) [--session-id <id>]\n omo-memory mcp\n`);
98
+ 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 hooks install --host <codex|grok|all>\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 handoff write (--summary <text> | --summary-file <path>) [--session-id <id>]\n omo-memory mcp\n`);
90
99
  }
91
100
  main(process.argv.slice(2)).catch((error) => {
92
101
  const message = error instanceof Error ? error.message : String(error);
package/dist/hooks.js ADDED
@@ -0,0 +1,161 @@
1
+ import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import { join } from "node:path";
4
+ const CODEX_SKILL = `---
5
+ name: omo-memory
6
+ description: Use OMO Memory as the shared local memory ledger across Codex, Grok, and OpenCode. Trigger when starting non-trivial workspace work, needing prior session context, recording decisions, QA evidence, task state, handoffs, or using the omo-memory MCP tools.
7
+ ---
8
+
9
+ # OMO Memory
10
+
11
+ Use the \`omo-memory\` MCP server as the shared local memory ledger across Codex, Grok, and OpenCode.
12
+
13
+ ## Session Start
14
+
15
+ At the beginning of non-trivial workspace work, call \`memory_bootstrap_session\` with:
16
+
17
+ - \`host\`: \`codex\`
18
+ - \`adapter\`: \`lazycodex\`
19
+ - \`limit\`: \`5\`
20
+
21
+ Read \`recentEvents\` before changing files. Keep the returned \`sessionId\` for the turn/session.
22
+
23
+ ## During Work
24
+
25
+ Record durable, concise facts with \`memory_record_event\` and the active \`sessionId\`: decisions, QA evidence, task state, blockers, and handoff-worthy facts.
26
+
27
+ Use \`memory_write_handoff\` for explicit handoff summaries.
28
+
29
+ ## Privacy
30
+
31
+ Never store full transcripts, API keys, tokens, \`.env\` contents, auth files, cookies, bearer headers, or raw secret-bearing logs. Store summaries and evidence references only.
32
+ `;
33
+ const GROK_SKILL = CODEX_SKILL.replace("Codex, Grok", "Grok, Codex")
34
+ .replace("- `host`: `codex`", "- `host`: `grok`")
35
+ .replace("- `adapter`: `lazycodex`", "- `adapter`: `lfg`");
36
+ const CODEX_AGENTS_BLOCK = `### OMO Memory lifecycle
37
+
38
+ When the \`omo-memory\` MCP server is available, use it as the shared local memory ledger for Codex/Grok/OpenCode work.
39
+
40
+ At the beginning of a non-trivial workspace session, call \`memory_bootstrap_session\` with:
41
+
42
+ - \`host\`: \`codex\`
43
+ - \`adapter\`: \`lazycodex\`
44
+ - \`limit\`: \`5\`
45
+
46
+ Read the returned \`recentEvents\` before making changes. Keep the returned \`sessionId\` for this session.
47
+
48
+ During work, record concise durable state with \`memory_record_event\` using that \`sessionId\`: decisions, task state, QA evidence, important blockers, and handoff-worthy facts. Use \`memory_write_handoff\` only for explicit handoff summaries. Do not store full transcripts, API keys, tokens, \`.env\` contents, auth files, cookies, bearer headers, or raw secret-bearing logs.`;
49
+ const GROK_AGENTS_BLOCK = CODEX_AGENTS_BLOCK.replace("- `host`: `codex`", "- `host`: `grok`").replace("- `adapter`: `lazycodex`", "- `adapter`: `lfg`");
50
+ const GROK_HOOK_SCRIPT = `#!/usr/bin/env node
51
+ import { spawnSync } from "node:child_process";
52
+
53
+ const host = process.env["OMO_MEMORY_HOST"] ?? "grok";
54
+ const adapter = process.env["OMO_MEMORY_ADAPTER"] ?? "lfg";
55
+ const limit = process.env["OMO_MEMORY_LIMIT"] ?? "5";
56
+
57
+ const result = spawnSync("npx", ["-y", "omo-memory", "session", "bootstrap", "--host", host, "--adapter", adapter, "--limit", limit], {
58
+ encoding: "utf8",
59
+ stdio: ["ignore", "pipe", "pipe"],
60
+ timeout: 5000
61
+ });
62
+
63
+ if (result.status !== 0) {
64
+ process.stdout.write("OMO Memory: bootstrap unavailable; continue without blocking the session.\\n");
65
+ process.exit(0);
66
+ }
67
+
68
+ try {
69
+ const payload = JSON.parse(result.stdout);
70
+ const recentEvents = Array.isArray(payload.recentEvents) ? payload.recentEvents : [];
71
+ process.stdout.write(\`OMO Memory sessionId: \${payload.sessionId}\\n\`);
72
+ if (recentEvents.length === 0) {
73
+ process.stdout.write("OMO Memory recentEvents: none for this project.\\n");
74
+ process.exit(0);
75
+ }
76
+ process.stdout.write("OMO Memory recentEvents:\\n");
77
+ for (const event of recentEvents.slice(0, Number(limit))) {
78
+ process.stdout.write(\`- \${event.type}: \${event.summary}\\n\`);
79
+ }
80
+ } catch (error) {
81
+ const detail = error instanceof Error ? error.message : String(error);
82
+ process.stdout.write(\`OMO Memory: bootstrap returned unreadable output; \${detail}\\n\`);
83
+ }
84
+ `;
85
+ const GROK_HOOKS_JSON = `{
86
+ "hooks": {
87
+ "SessionStart": [
88
+ {
89
+ "hooks": [
90
+ {
91
+ "type": "command",
92
+ "command": "node \\"{{HOME}}/.grok/hooks/omo-memory-session.mjs\\"",
93
+ "timeout": 5,
94
+ "description": "omo-memory session bootstrap",
95
+ "statusMessage": "OMO Memory: loading recent session memory"
96
+ }
97
+ ]
98
+ }
99
+ ]
100
+ }
101
+ }
102
+ `;
103
+ const BLOCK_START = "<!-- omo-memory:start -->";
104
+ const BLOCK_END = "<!-- omo-memory:end -->";
105
+ export function installHooks(input) {
106
+ const home = process.env["OMO_MEMORY_INSTALL_HOME"] ?? homedir();
107
+ return { home, installed: targetsFor(input.host).map((target) => installTarget(target, home)) };
108
+ }
109
+ function targetsFor(host) {
110
+ if (host === "all")
111
+ return ["codex", "grok"];
112
+ return [host];
113
+ }
114
+ function installTarget(host, home) {
115
+ if (host === "codex")
116
+ return installCodex(home);
117
+ return installGrok(home);
118
+ }
119
+ function installCodex(home) {
120
+ const skillPath = join(home, ".codex", "skills", "omo-memory", "SKILL.md");
121
+ const agentsPath = join(home, ".codex", "AGENTS.md");
122
+ writeText(skillPath, CODEX_SKILL);
123
+ upsertAgentsBlock(agentsPath, CODEX_AGENTS_BLOCK);
124
+ return {
125
+ host: "codex",
126
+ files: [skillPath, agentsPath],
127
+ notes: ["Codex has no standalone user hook add command; this installs the passive lifecycle rule through global AGENTS.md."],
128
+ };
129
+ }
130
+ function installGrok(home) {
131
+ const skillPath = join(home, ".grok", "skills", "omo-memory", "SKILL.md");
132
+ const agentsPath = join(home, ".grok", "AGENTS.md");
133
+ const hookScriptPath = join(home, ".grok", "hooks", "omo-memory-session.mjs");
134
+ const hookJsonPath = join(home, ".grok", "hooks", "omo-memory-hooks.json");
135
+ writeText(skillPath, GROK_SKILL);
136
+ upsertAgentsBlock(agentsPath, GROK_AGENTS_BLOCK);
137
+ writeText(hookScriptPath, GROK_HOOK_SCRIPT);
138
+ chmodSync(hookScriptPath, 0o755);
139
+ writeText(hookJsonPath, GROK_HOOKS_JSON.replace("{{HOME}}", home));
140
+ return {
141
+ host: "grok",
142
+ files: [skillPath, agentsPath, hookScriptPath, hookJsonPath],
143
+ notes: ["Grok discovers ~/.grok/skills and ~/.grok/hooks; run grok inspect to verify discovery."],
144
+ };
145
+ }
146
+ function writeText(path, text) {
147
+ mkdirSync(join(path, ".."), { recursive: true });
148
+ writeFileSync(path, text, "utf8");
149
+ }
150
+ function upsertAgentsBlock(path, block) {
151
+ const existing = existsSync(path) ? readFileSync(path, "utf8") : "";
152
+ const wrapped = `${BLOCK_START}\n${block}\n${BLOCK_END}`;
153
+ const start = existing.indexOf(BLOCK_START);
154
+ const end = existing.indexOf(BLOCK_END);
155
+ if (start !== -1 && end > start) {
156
+ writeText(path, `${existing.slice(0, start)}${wrapped}${existing.slice(end + BLOCK_END.length)}`);
157
+ return;
158
+ }
159
+ const separator = existing.trim().length === 0 ? "" : "\n\n";
160
+ writeText(path, `${existing.trimEnd()}${separator}${wrapped}\n`);
161
+ }
package/dist/mcp.js CHANGED
@@ -3,7 +3,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
3
3
  import { z } from "zod";
4
4
  import { bootstrapSession, exportMemory, initMemory, memoryPaths, purgeMemory, PurgeConfirmationError, recentEvents, recordEvent, resolveProjectContext, startSession, writeHandoff } from "./memory.js";
5
5
  export async function runMcpServer() {
6
- const server = new McpServer({ name: "omo-memory", version: "0.1.1" });
6
+ const server = new McpServer({ name: "omo-memory", version: "0.1.2" });
7
7
  server.registerTool("memory_init", {
8
8
  title: "Initialize OMO Memory",
9
9
  description: "Create or migrate the local OMO memory SQLite database.",
@@ -27,6 +27,7 @@ After npm publish, adapters and users can invoke the packaged CLI directly:
27
27
 
28
28
  ```sh
29
29
  npx -y omo-memory init
30
+ npx -y omo-memory hooks install --host all
30
31
  npx -y omo-memory session bootstrap --host codex --adapter lazycodex --limit 5
31
32
  npx -y omo-memory session start --host codex --adapter lazycodex
32
33
  npx -y omo-memory session start --host grok --adapter lfg
@@ -82,6 +83,22 @@ grok mcp add omo-memory -- npx -y omo-memory mcp
82
83
 
83
84
  Register the same MCP server in every host that needs memory access. Do not create separate Codex/Grok schemas or databases; host identity belongs in `memory_start_session` metadata.
84
85
 
86
+ ## Passive Hook Install
87
+
88
+ Install host-side passive behavior with:
89
+
90
+ ```sh
91
+ npx -y omo-memory hooks install --host all
92
+ ```
93
+
94
+ Supported `--host` values:
95
+
96
+ - `codex`: installs `~/.codex/skills/omo-memory/SKILL.md` and an idempotent OMO Memory block in `~/.codex/AGENTS.md`.
97
+ - `grok`: installs `~/.grok/skills/omo-memory/SKILL.md`, an idempotent OMO Memory block in `~/.grok/AGENTS.md`, and `~/.grok/hooks/omo-memory-hooks.json` plus its SessionStart script.
98
+ - `all`: installs both.
99
+
100
+ The installer is idempotent and replaces only the marked `omo-memory` block in AGENTS files.
101
+
85
102
  ## Session Bootstrap Flow
86
103
 
87
104
  At the beginning of a Codex, OpenCode, or Grok adapter session, call `memory_bootstrap_session` instead of separately calling `memory_start_session` and `memory_recent_events`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "omo-memory",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
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": [