omo-memory 0.1.2 → 0.1.4

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.
@@ -0,0 +1,151 @@
1
+ export const CODEX_MARKETPLACE = "islee23520";
2
+ export const CODEX_PLUGIN = "omo-memory";
3
+ export const CODEX_SKILL = `---
4
+ name: omo-memory
5
+ 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.
6
+ ---
7
+
8
+ # OMO Memory
9
+
10
+ Use the \`omo-memory\` MCP server as the shared local memory ledger across Codex, Grok, and OpenCode.
11
+
12
+ ## Session Start
13
+
14
+ At the beginning of non-trivial workspace work, call \`memory_bootstrap_session\` with:
15
+
16
+ - \`host\`: \`codex\`
17
+ - \`adapter\`: \`lazycodex\`
18
+ - \`limit\`: \`5\`
19
+
20
+ Read \`recentEvents\` before changing files. Keep the returned \`sessionId\` for the turn/session.
21
+
22
+ ## During Work
23
+
24
+ Record durable, concise facts with \`memory_record_event\` and the active \`sessionId\`: decisions, QA evidence, task state, blockers, and handoff-worthy facts.
25
+
26
+ Use \`memory_write_handoff\` for explicit handoff summaries.
27
+
28
+ ## Privacy
29
+
30
+ 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.
31
+ `;
32
+ export const GROK_SKILL = CODEX_SKILL.replace("Codex, Grok", "Grok, Codex")
33
+ .replace("- `host`: `codex`", "- `host`: `grok`")
34
+ .replace("- `adapter`: `lazycodex`", "- `adapter`: `lfg`");
35
+ export const CODEX_AGENTS_BLOCK = `### OMO Memory lifecycle
36
+
37
+ When the \`omo-memory\` MCP server is available, use it as the shared local memory ledger for Codex/Grok/OpenCode work.
38
+
39
+ At the beginning of a non-trivial workspace session, call \`memory_bootstrap_session\` with:
40
+
41
+ - \`host\`: \`codex\`
42
+ - \`adapter\`: \`lazycodex\`
43
+ - \`limit\`: \`5\`
44
+
45
+ Read the returned \`recentEvents\` before making changes. Keep the returned \`sessionId\` for this session.
46
+
47
+ 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.`;
48
+ export const GROK_AGENTS_BLOCK = CODEX_AGENTS_BLOCK.replace("- `host`: `codex`", "- `host`: `grok`").replace("- `adapter`: `lazycodex`", "- `adapter`: `lfg`");
49
+ export const SESSION_BOOTSTRAP_SCRIPT = `#!/usr/bin/env node
50
+ import { spawnSync } from "node:child_process";
51
+
52
+ const host = process.env["OMO_MEMORY_HOST"] ?? "codex";
53
+ const adapter = process.env["OMO_MEMORY_ADAPTER"] ?? "lazycodex";
54
+ const limit = process.env["OMO_MEMORY_LIMIT"] ?? "5";
55
+
56
+ const result = spawnSync("npx", ["-y", "omo-memory", "session", "bootstrap", "--host", host, "--adapter", adapter, "--limit", limit], {
57
+ encoding: "utf8",
58
+ stdio: ["ignore", "pipe", "pipe"],
59
+ timeout: 5000
60
+ });
61
+
62
+ if (result.status !== 0) {
63
+ process.stdout.write("OMO Memory: bootstrap unavailable; continue without blocking the session.\\n");
64
+ process.exit(0);
65
+ }
66
+
67
+ try {
68
+ const payload = JSON.parse(result.stdout);
69
+ const recentEvents = Array.isArray(payload.recentEvents) ? payload.recentEvents : [];
70
+ process.stdout.write(\`OMO Memory sessionId: \${payload.sessionId}\\n\`);
71
+ if (recentEvents.length === 0) {
72
+ process.stdout.write("OMO Memory recentEvents: none for this project.\\n");
73
+ process.exit(0);
74
+ }
75
+ process.stdout.write("OMO Memory recentEvents:\\n");
76
+ for (const event of recentEvents.slice(0, Number(limit))) {
77
+ process.stdout.write(\`- \${event.type}: \${event.summary}\\n\`);
78
+ }
79
+ } catch (error) {
80
+ const detail = error instanceof Error ? error.message : String(error);
81
+ process.stdout.write(\`OMO Memory: bootstrap returned unreadable output; \${detail}\\n\`);
82
+ }
83
+ `;
84
+ export const GROK_HOOK_SCRIPT = SESSION_BOOTSTRAP_SCRIPT.replace('?? "codex"', '?? "grok"').replace('?? "lazycodex"', '?? "lfg"');
85
+ export const CODEX_PLUGIN_JSON = `{
86
+ "name": "omo-memory",
87
+ "version": "0.1.4",
88
+ "description": "Session-start OMO Memory bootstrap hook for Codex.",
89
+ "author": "islee23520",
90
+ "homepage": "https://github.com/islee23520/omo-memory",
91
+ "repository": "https://github.com/islee23520/omo-memory",
92
+ "license": "MIT",
93
+ "keywords": ["codex", "hooks", "memory", "mcp"],
94
+ "skills": "./skills/",
95
+ "hooks": "./hooks/hooks.json",
96
+ "interface": {
97
+ "displayName": "OMO Memory",
98
+ "shortDescription": "Bootstraps local shared memory at Codex session start.",
99
+ "longDescription": "OMO Memory records concise local session state and loads recent project memory through the omo-memory CLI and MCP server.",
100
+ "developerName": "islee23520",
101
+ "category": "Developer Tools",
102
+ "capabilities": ["Hooks", "MCP Tools", "Workflow"],
103
+ "websiteURL": "https://github.com/islee23520/omo-memory",
104
+ "privacyPolicyURL": "https://github.com/islee23520/omo-memory#privacy",
105
+ "termsOfServiceURL": "https://github.com/islee23520/omo-memory#license",
106
+ "defaultPrompt": [
107
+ "Use OMO Memory to load recent project memory before non-trivial workspace work.",
108
+ "Record durable decisions, task state, QA evidence, blockers, and handoffs without storing secrets or full transcripts."
109
+ ],
110
+ "brandColor": "#0F766E",
111
+ "screenshots": []
112
+ },
113
+ "capabilities": []
114
+ }
115
+ `;
116
+ export const CODEX_HOOKS_JSON = `{
117
+ "hooks": {
118
+ "SessionStart": [
119
+ {
120
+ "hooks": [
121
+ {
122
+ "type": "command",
123
+ "command": "node \\"\${PLUGIN_ROOT}/scripts/omo-memory-session.mjs\\"",
124
+ "timeout": 5,
125
+ "description": "omo-memory session bootstrap",
126
+ "statusMessage": "OMO Memory: loading recent session memory"
127
+ }
128
+ ]
129
+ }
130
+ ]
131
+ }
132
+ }
133
+ `;
134
+ export const GROK_HOOKS_JSON = `{
135
+ "hooks": {
136
+ "SessionStart": [
137
+ {
138
+ "hooks": [
139
+ {
140
+ "type": "command",
141
+ "command": "node \\"{{HOME}}/.grok/hooks/omo-memory-session.mjs\\"",
142
+ "timeout": 5,
143
+ "description": "omo-memory session bootstrap",
144
+ "statusMessage": "OMO Memory: loading recent session memory"
145
+ }
146
+ ]
147
+ }
148
+ ]
149
+ }
150
+ }
151
+ `;
package/dist/hooks.js CHANGED
@@ -1,105 +1,8 @@
1
+ import { spawnSync } from "node:child_process";
1
2
  import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
3
  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
- `;
4
+ import { dirname, join } from "node:path";
5
+ import { CODEX_AGENTS_BLOCK, CODEX_HOOKS_JSON, CODEX_MARKETPLACE, CODEX_PLUGIN, CODEX_PLUGIN_JSON, CODEX_SKILL, GROK_AGENTS_BLOCK, GROK_HOOK_SCRIPT, GROK_HOOKS_JSON, GROK_SKILL, SESSION_BOOTSTRAP_SCRIPT, } from "./hookTemplates.js";
103
6
  const BLOCK_START = "<!-- omo-memory:start -->";
104
7
  const BLOCK_END = "<!-- omo-memory:end -->";
105
8
  export function installHooks(input) {
@@ -119,12 +22,35 @@ function installTarget(host, home) {
119
22
  function installCodex(home) {
120
23
  const skillPath = join(home, ".codex", "skills", "omo-memory", "SKILL.md");
121
24
  const agentsPath = join(home, ".codex", "AGENTS.md");
25
+ const marketplacePath = join(home, ".codex", "local-marketplaces", CODEX_MARKETPLACE);
26
+ const marketplaceJsonPath = join(marketplacePath, "marketplace.json");
27
+ const agentsMarketplaceJsonPath = join(marketplacePath, ".agents", "plugins", "marketplace.json");
28
+ const pluginRoot = join(marketplacePath, "plugins", CODEX_PLUGIN);
29
+ const pluginJsonPath = join(pluginRoot, ".codex-plugin", "plugin.json");
30
+ const pluginSkillPath = join(pluginRoot, "skills", "omo-memory", "SKILL.md");
31
+ const hookJsonPath = join(pluginRoot, "hooks", "hooks.json");
32
+ const hookScriptPath = join(pluginRoot, "scripts", "omo-memory-session.mjs");
33
+ const configPath = join(home, ".codex", "config.toml");
122
34
  writeText(skillPath, CODEX_SKILL);
123
35
  upsertAgentsBlock(agentsPath, CODEX_AGENTS_BLOCK);
36
+ writeText(pluginJsonPath, CODEX_PLUGIN_JSON);
37
+ writeText(pluginSkillPath, CODEX_SKILL);
38
+ writeText(hookJsonPath, CODEX_HOOKS_JSON);
39
+ writeText(hookScriptPath, SESSION_BOOTSTRAP_SCRIPT);
40
+ chmodSync(hookScriptPath, 0o755);
41
+ upsertMarketplace(marketplaceJsonPath, { withInterface: false });
42
+ upsertMarketplace(agentsMarketplaceJsonPath, { withInterface: true });
43
+ upsertCodexConfig(configPath, marketplacePath);
44
+ const pluginAddNote = maybeInstallCodexPlugin(home);
124
45
  return {
125
46
  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."],
47
+ files: [skillPath, agentsPath, pluginJsonPath, pluginSkillPath, hookJsonPath, hookScriptPath, marketplaceJsonPath, agentsMarketplaceJsonPath, configPath],
48
+ notes: [
49
+ "Codex MCP still needs `codex mcp add omo-memory -- npx -y omo-memory mcp` if it is not already present.",
50
+ "Codex plugin hooks are installed through the local islee23520 marketplace and enabled in ~/.codex/config.toml.",
51
+ pluginAddNote,
52
+ "Codex may ask to trust the new hook command on first execution unless hook trust has already been granted.",
53
+ ],
128
54
  };
129
55
  }
130
56
  function installGrok(home) {
@@ -144,9 +70,94 @@ function installGrok(home) {
144
70
  };
145
71
  }
146
72
  function writeText(path, text) {
147
- mkdirSync(join(path, ".."), { recursive: true });
73
+ mkdirSync(dirname(path), { recursive: true });
148
74
  writeFileSync(path, text, "utf8");
149
75
  }
76
+ function upsertMarketplace(path, input) {
77
+ const marketplace = readMarketplace(path, input.withInterface);
78
+ const plugins = marketplace.plugins.filter((plugin) => plugin.name !== CODEX_PLUGIN);
79
+ const omoMemory = marketplacePlugin(input.withInterface);
80
+ const next = { ...marketplace, plugins: [...plugins, omoMemory] };
81
+ writeText(path, `${JSON.stringify(next, null, 2)}\n`);
82
+ }
83
+ function readMarketplace(path, withInterface) {
84
+ if (!existsSync(path))
85
+ return defaultMarketplace(withInterface);
86
+ const parsed = JSON.parse(readFileSync(path, "utf8"));
87
+ if (!isMarketplace(parsed))
88
+ return defaultMarketplace(withInterface);
89
+ return parsed;
90
+ }
91
+ function defaultMarketplace(withInterface) {
92
+ return {
93
+ name: CODEX_MARKETPLACE,
94
+ ...(withInterface ? { interface: { displayName: CODEX_MARKETPLACE } } : {}),
95
+ plugins: [],
96
+ };
97
+ }
98
+ function marketplacePlugin(withPolicy) {
99
+ return {
100
+ name: CODEX_PLUGIN,
101
+ source: { source: "local", path: "./plugins/omo-memory" },
102
+ ...(withPolicy ? { policy: { installation: "AVAILABLE", authentication: "ON_INSTALL" }, category: "Developer Tools" } : {}),
103
+ };
104
+ }
105
+ function isMarketplace(value) {
106
+ if (!isRecord(value) || value["name"] !== CODEX_MARKETPLACE || !Array.isArray(value["plugins"]))
107
+ return false;
108
+ return value["plugins"].every(isMarketplacePlugin);
109
+ }
110
+ function isMarketplacePlugin(value) {
111
+ if (!isRecord(value) || typeof value["name"] !== "string" || !isRecord(value["source"]))
112
+ return false;
113
+ const source = value["source"];
114
+ return source["source"] === "local" && typeof source["path"] === "string";
115
+ }
116
+ function isRecord(value) {
117
+ return typeof value === "object" && value !== null;
118
+ }
119
+ function upsertCodexConfig(path, marketplacePath) {
120
+ const existing = existsSync(path) ? readFileSync(path, "utf8") : "";
121
+ const withHooks = ensureTomlKey(existing, "[features]", "plugin_hooks = true");
122
+ const withMarketplace = ensureTomlTable(withHooks, `[marketplaces.${CODEX_MARKETPLACE}]`, `source_type = "local"\nsource = "${escapeTomlString(marketplacePath)}"`);
123
+ writeText(path, ensureTomlTable(withMarketplace, `[plugins."${CODEX_PLUGIN}@${CODEX_MARKETPLACE}"]`, "enabled = true"));
124
+ }
125
+ function maybeInstallCodexPlugin(home) {
126
+ if (home !== homedir())
127
+ return "Skipped `codex plugin add` because OMO_MEMORY_INSTALL_HOME points at a test/alternate home.";
128
+ const result = spawnSync("codex", ["plugin", "add", `${CODEX_PLUGIN}@${CODEX_MARKETPLACE}`, "--json"], {
129
+ encoding: "utf8",
130
+ stdio: ["ignore", "pipe", "pipe"],
131
+ timeout: 15000,
132
+ });
133
+ if (result.status === 0)
134
+ return "`codex plugin add omo-memory@islee23520 --json` completed successfully.";
135
+ const detail = result.error instanceof Error ? result.error.message : result.stderr.trim();
136
+ return `Could not run \`codex plugin add omo-memory@islee23520 --json\`: ${detail || "unknown error"}. Run it manually if Codex still reports the plugin as not installed.`;
137
+ }
138
+ function ensureTomlKey(text, table, keyValue) {
139
+ if (text.includes(keyValue))
140
+ return text;
141
+ if (!text.includes(table))
142
+ return appendTomlTable(text, table, keyValue);
143
+ const start = text.indexOf(table);
144
+ const nextTable = text.indexOf("\n[", start + table.length);
145
+ const insertAt = nextTable === -1 ? text.length : nextTable;
146
+ return `${text.slice(0, insertAt).trimEnd()}\n${keyValue}\n${text.slice(insertAt).trimStart()}`;
147
+ }
148
+ function ensureTomlTable(text, table, body) {
149
+ if (text.includes(table))
150
+ return text;
151
+ return appendTomlTable(text, table, body);
152
+ }
153
+ function appendTomlTable(text, table, body) {
154
+ const prefix = text.trimEnd();
155
+ const separator = prefix.length === 0 ? "" : "\n\n";
156
+ return `${prefix}${separator}${table}\n${body}\n`;
157
+ }
158
+ function escapeTomlString(value) {
159
+ return value.replaceAll("\\", "\\\\").replaceAll('"', '\\"');
160
+ }
150
161
  function upsertAgentsBlock(path, block) {
151
162
  const existing = existsSync(path) ? readFileSync(path, "utf8") : "";
152
163
  const wrapped = `${BLOCK_START}\n${block}\n${BLOCK_END}`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "omo-memory",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
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": [