omo-memory 0.1.9 → 0.1.11
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 +0 -16
- package/dist/cli.js +1 -10
- package/docs/adapter-integration.md +0 -17
- package/package.json +1 -1
- package/dist/hookTemplates.js +0 -167
- package/dist/hooks.js +0 -198
package/README.md
CHANGED
|
@@ -33,7 +33,6 @@ After the package is published to npm, use the same package for CLI and MCP:
|
|
|
33
33
|
|
|
34
34
|
```sh
|
|
35
35
|
npx -y omo-memory init
|
|
36
|
-
npx -y omo-memory hooks install --host all
|
|
37
36
|
npx -y omo-memory session bootstrap --host codex --adapter lazycodex --limit 5
|
|
38
37
|
npx -y omo-memory recent --limit 5
|
|
39
38
|
npx -y omo-memory mcp
|
|
@@ -66,21 +65,6 @@ grok mcp add omo-memory -- npx -y omo-memory mcp
|
|
|
66
65
|
|
|
67
66
|
Both hosts use the current project ledger at `<project-root>/.omo/memory/state.sqlite` by default. The `host` value is recorded when an adapter calls `memory_start_session`, not by installing separate servers.
|
|
68
67
|
|
|
69
|
-
## Passive setup
|
|
70
|
-
|
|
71
|
-
Install the host plugin-style bundles with:
|
|
72
|
-
|
|
73
|
-
```sh
|
|
74
|
-
npx -y omo-memory hooks install --host all
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
This installs:
|
|
78
|
-
|
|
79
|
-
- Codex: `~/.codex/skills/omo-memory/SKILL.md`, a global `~/.codex/AGENTS.md` lifecycle rule, and a local Codex plugin with a `SessionStart` hook.
|
|
80
|
-
- Grok: `~/.grok/plugins/omo-memory` with `plugin.json`, skill, `SessionStart` hook, and `.mcp.json`, plus compatibility copies in `~/.grok/skills` and `~/.grok/hooks`.
|
|
81
|
-
|
|
82
|
-
The installer is idempotent. For Codex it also runs `codex plugin add omo-memory@islee23520 --json` when installing into the real home directory.
|
|
83
|
-
|
|
84
68
|
## Session bootstrap
|
|
85
69
|
|
|
86
70
|
Adapters should call the bootstrap tool at the beginning of each host session:
|
package/dist/cli.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { readFileSync } from "node:fs";
|
|
3
|
-
import { installHooks } from "./hooks.js";
|
|
4
3
|
import { runMcpServer } from "./mcp.js";
|
|
5
4
|
import { bootstrapSession, doctorReport, exportMemory, purgeMemory, recentEvents, recordEvent, startSession, writeHandoff } from "./memory.js";
|
|
6
5
|
import { initMemory } from "./memoryDb.js";
|
|
@@ -31,9 +30,6 @@ function runCommand(command, subcommand, rest) {
|
|
|
31
30
|
const args = [subcommand, ...rest].filter((value) => value !== undefined);
|
|
32
31
|
return { ok: true, ...purgeMemory({ yes: args.includes("--yes") }) };
|
|
33
32
|
}
|
|
34
|
-
if (command === "hooks" && subcommand === "install") {
|
|
35
|
-
return { ok: true, ...installHooks({ host: parseHookInstallHost(readFlag(rest, "--host") ?? "all") }) };
|
|
36
|
-
}
|
|
37
33
|
if (command === "session" && subcommand === "start") {
|
|
38
34
|
const host = parseHost(readFlag(rest, "--host") ?? "unknown");
|
|
39
35
|
const adapter = readFlag(rest, "--adapter") ?? "unknown";
|
|
@@ -81,11 +77,6 @@ function parseHost(value) {
|
|
|
81
77
|
return value;
|
|
82
78
|
fail("--host must be one of codex, opencode, grok, unknown");
|
|
83
79
|
}
|
|
84
|
-
function parseHookInstallHost(value) {
|
|
85
|
-
if (value === "codex" || value === "grok" || value === "all")
|
|
86
|
-
return value;
|
|
87
|
-
fail("--host must be one of codex, grok, all");
|
|
88
|
-
}
|
|
89
80
|
function readPositiveIntFlag(args, name, defaultValue) {
|
|
90
81
|
return parsePositiveInt(readFlag(args, name) ?? String(defaultValue), name);
|
|
91
82
|
}
|
|
@@ -99,7 +90,7 @@ function fail(message) {
|
|
|
99
90
|
throw new Error(message);
|
|
100
91
|
}
|
|
101
92
|
function printHelp() {
|
|
102
|
-
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
|
|
93
|
+
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`);
|
|
103
94
|
}
|
|
104
95
|
main(process.argv.slice(2)).catch((error) => {
|
|
105
96
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -28,7 +28,6 @@ After npm publish, adapters and users can invoke the packaged CLI directly:
|
|
|
28
28
|
|
|
29
29
|
```sh
|
|
30
30
|
npx -y omo-memory init
|
|
31
|
-
npx -y omo-memory hooks install --host all
|
|
32
31
|
npx -y omo-memory session bootstrap --host codex --adapter lazycodex --limit 5
|
|
33
32
|
npx -y omo-memory session start --host codex --adapter lazycodex
|
|
34
33
|
npx -y omo-memory session start --host grok --adapter lfg
|
|
@@ -84,22 +83,6 @@ grok mcp add omo-memory -- npx -y omo-memory mcp
|
|
|
84
83
|
|
|
85
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.
|
|
86
85
|
|
|
87
|
-
## Plugin-Style Host Install
|
|
88
|
-
|
|
89
|
-
Install host-side passive behavior with:
|
|
90
|
-
|
|
91
|
-
```sh
|
|
92
|
-
npx -y omo-memory hooks install --host all
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
Supported `--host` values:
|
|
96
|
-
|
|
97
|
-
- `codex`: installs `~/.codex/skills/omo-memory/SKILL.md`, an idempotent OMO Memory block in `~/.codex/AGENTS.md`, and a local Codex plugin with a `SessionStart` hook.
|
|
98
|
-
- `grok`: installs `~/.grok/plugins/omo-memory` with `plugin.json`, a bundled skill, `hooks/hooks.json`, `.mcp.json`, and a SessionStart script. It also writes compatibility copies to `~/.grok/skills` and `~/.grok/hooks`.
|
|
99
|
-
- `all`: installs both.
|
|
100
|
-
|
|
101
|
-
The installer is idempotent and replaces only the marked `omo-memory` block in AGENTS files. For Codex it also registers the local plugin with `codex plugin add omo-memory@islee23520 --json` when installing into the real home directory. For Grok, `~/.grok/plugins/omo-memory` is a user plugin and is trusted automatically by Grok.
|
|
102
|
-
|
|
103
86
|
## Session Bootstrap Flow
|
|
104
87
|
|
|
105
88
|
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
package/dist/hookTemplates.js
DELETED
|
@@ -1,167 +0,0 @@
|
|
|
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 for concise local project memory: bootstrap recent events, record decisions, QA evidence, blockers, and handoffs without secrets.
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
# OMO Memory
|
|
9
|
-
|
|
10
|
-
For non-trivial workspace work, call \`memory_bootstrap_session\` before edits:
|
|
11
|
-
|
|
12
|
-
- \`host\`: \`codex\`
|
|
13
|
-
- \`adapter\`: \`lazycodex\`
|
|
14
|
-
- \`limit\`: \`2\`
|
|
15
|
-
|
|
16
|
-
Read \`recentEvents\`, keep the returned \`sessionId\`, and record only durable summaries with \`memory_record_event\`: decisions, QA evidence, task state, blockers, and handoffs.
|
|
17
|
-
|
|
18
|
-
Use \`memory_write_handoff\` only for explicit handoff summaries. Never store transcripts, secrets, \`.env\`, auth files, cookies, bearer headers, or raw secret-bearing logs.
|
|
19
|
-
`;
|
|
20
|
-
export const GROK_SKILL = CODEX_SKILL.replace("Codex, Grok", "Grok, Codex")
|
|
21
|
-
.replace("- `host`: `codex`", "- `host`: `grok`")
|
|
22
|
-
.replace("- `adapter`: `lazycodex`", "- `adapter`: `lfg`");
|
|
23
|
-
export const CODEX_AGENTS_BLOCK = `### OMO Memory lifecycle
|
|
24
|
-
|
|
25
|
-
When the \`omo-memory\` MCP server is available, use it for concise local project memory. For non-trivial workspace work, call \`memory_bootstrap_session\` before edits:
|
|
26
|
-
|
|
27
|
-
- \`host\`: \`codex\`
|
|
28
|
-
- \`adapter\`: \`lazycodex\`
|
|
29
|
-
- \`limit\`: \`2\`
|
|
30
|
-
|
|
31
|
-
Read \`recentEvents\`, keep the returned \`sessionId\`, and record only durable summaries with \`memory_record_event\`: decisions, task state, QA evidence, blockers, and handoffs. Never store transcripts, secrets, \`.env\`, auth files, cookies, bearer headers, or raw secret-bearing logs.`;
|
|
32
|
-
export const GROK_AGENTS_BLOCK = CODEX_AGENTS_BLOCK.replace("- `host`: `codex`", "- `host`: `grok`").replace("- `adapter`: `lazycodex`", "- `adapter`: `lfg`");
|
|
33
|
-
export const SESSION_BOOTSTRAP_SCRIPT = `#!/usr/bin/env node
|
|
34
|
-
import { spawnSync } from "node:child_process";
|
|
35
|
-
|
|
36
|
-
const host = process.env["OMO_MEMORY_HOST"] ?? "codex";
|
|
37
|
-
const adapter = process.env["OMO_MEMORY_ADAPTER"] ?? "lazycodex";
|
|
38
|
-
const limit = process.env["OMO_MEMORY_LIMIT"] ?? "2";
|
|
39
|
-
const args = ["session", "bootstrap", "--host", host, "--adapter", adapter, "--limit", limit];
|
|
40
|
-
|
|
41
|
-
const direct = process.env["OMO_MEMORY_CLI"] ?? "omo-memory";
|
|
42
|
-
const result = runBootstrap(direct, args) ?? runBootstrap("npx", ["-y", "omo-memory", ...args]);
|
|
43
|
-
|
|
44
|
-
if (result === undefined || result.status !== 0) {
|
|
45
|
-
process.stdout.write("OMO Memory: bootstrap unavailable; continue without blocking the session.\\n");
|
|
46
|
-
process.exit(0);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
try {
|
|
50
|
-
const payload = JSON.parse(result.stdout);
|
|
51
|
-
const recentEvents = Array.isArray(payload.recentEvents) ? payload.recentEvents : [];
|
|
52
|
-
process.stdout.write(\`OMO Memory sessionId: \${payload.sessionId}\\n\`);
|
|
53
|
-
if (recentEvents.length === 0) {
|
|
54
|
-
process.exit(0);
|
|
55
|
-
}
|
|
56
|
-
process.stdout.write("OMO Memory recentEvents:\\n");
|
|
57
|
-
for (const event of recentEvents.slice(0, Number(limit))) {
|
|
58
|
-
process.stdout.write(\`- \${event.type}: \${event.summary}\\n\`);
|
|
59
|
-
}
|
|
60
|
-
} catch (error) {
|
|
61
|
-
const detail = error instanceof Error ? error.message : String(error);
|
|
62
|
-
process.stdout.write(\`OMO Memory: bootstrap returned unreadable output; \${detail}\\n\`);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
function runBootstrap(command, commandArgs) {
|
|
66
|
-
const result = spawnSync(command, commandArgs, {
|
|
67
|
-
encoding: "utf8",
|
|
68
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
69
|
-
timeout: 2000
|
|
70
|
-
});
|
|
71
|
-
if (result.error && result.error.code === "ENOENT") return undefined;
|
|
72
|
-
return result;
|
|
73
|
-
}
|
|
74
|
-
`;
|
|
75
|
-
export const GROK_HOOK_SCRIPT = SESSION_BOOTSTRAP_SCRIPT.replace('?? "codex"', '?? "grok"').replace('?? "lazycodex"', '?? "lfg"');
|
|
76
|
-
export const GROK_PLUGIN_JSON = `{
|
|
77
|
-
"name": "omo-memory",
|
|
78
|
-
"version": "0.1.9",
|
|
79
|
-
"description": "Project-local OMO Memory bootstrap hook and MCP server for Grok.",
|
|
80
|
-
"author": {
|
|
81
|
-
"name": "islee23520"
|
|
82
|
-
},
|
|
83
|
-
"repository": "https://github.com/islee23520/omo-memory",
|
|
84
|
-
"homepage": "https://github.com/islee23520/omo-memory",
|
|
85
|
-
"license": "MIT",
|
|
86
|
-
"keywords": ["grok", "hooks", "memory", "mcp"],
|
|
87
|
-
"skills": "./skills/",
|
|
88
|
-
"hooks": "./hooks/hooks.json",
|
|
89
|
-
"mcpServers": "./.mcp.json"
|
|
90
|
-
}
|
|
91
|
-
`;
|
|
92
|
-
export const GROK_MCP_JSON = `{
|
|
93
|
-
"mcpServers": {
|
|
94
|
-
"omo-memory": {
|
|
95
|
-
"command": "npx",
|
|
96
|
-
"args": ["-y", "omo-memory", "mcp"]
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
`;
|
|
101
|
-
export const CODEX_PLUGIN_JSON = `{
|
|
102
|
-
"name": "omo-memory",
|
|
103
|
-
"version": "0.1.9",
|
|
104
|
-
"description": "Session-start OMO Memory bootstrap hook for Codex.",
|
|
105
|
-
"author": "islee23520",
|
|
106
|
-
"homepage": "https://github.com/islee23520/omo-memory",
|
|
107
|
-
"repository": "https://github.com/islee23520/omo-memory",
|
|
108
|
-
"license": "MIT",
|
|
109
|
-
"keywords": ["codex", "hooks", "memory", "mcp"],
|
|
110
|
-
"skills": "./skills/",
|
|
111
|
-
"hooks": "./hooks/hooks.json",
|
|
112
|
-
"interface": {
|
|
113
|
-
"displayName": "OMO Memory",
|
|
114
|
-
"shortDescription": "Bootstraps local shared memory at Codex session start.",
|
|
115
|
-
"longDescription": "OMO Memory records concise local session state and loads recent project memory through the omo-memory CLI and MCP server.",
|
|
116
|
-
"developerName": "islee23520",
|
|
117
|
-
"category": "Developer Tools",
|
|
118
|
-
"capabilities": ["Hooks", "MCP Tools", "Workflow"],
|
|
119
|
-
"websiteURL": "https://github.com/islee23520/omo-memory",
|
|
120
|
-
"privacyPolicyURL": "https://github.com/islee23520/omo-memory#privacy",
|
|
121
|
-
"termsOfServiceURL": "https://github.com/islee23520/omo-memory#license",
|
|
122
|
-
"defaultPrompt": [
|
|
123
|
-
"Use OMO Memory to load recent project memory before non-trivial workspace work.",
|
|
124
|
-
"Record durable decisions, task state, QA evidence, blockers, and handoffs without storing secrets or full transcripts."
|
|
125
|
-
],
|
|
126
|
-
"brandColor": "#0F766E",
|
|
127
|
-
"screenshots": []
|
|
128
|
-
},
|
|
129
|
-
"capabilities": []
|
|
130
|
-
}
|
|
131
|
-
`;
|
|
132
|
-
export const CODEX_HOOKS_JSON = `{
|
|
133
|
-
"hooks": {
|
|
134
|
-
"SessionStart": [
|
|
135
|
-
{
|
|
136
|
-
"hooks": [
|
|
137
|
-
{
|
|
138
|
-
"type": "command",
|
|
139
|
-
"command": "node \\"\${PLUGIN_ROOT}/scripts/omo-memory-session.mjs\\"",
|
|
140
|
-
"timeout": 2,
|
|
141
|
-
"description": "omo-memory session bootstrap",
|
|
142
|
-
"statusMessage": "OMO Memory: loading recent session memory"
|
|
143
|
-
}
|
|
144
|
-
]
|
|
145
|
-
}
|
|
146
|
-
]
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
`;
|
|
150
|
-
export const GROK_HOOKS_JSON = `{
|
|
151
|
-
"hooks": {
|
|
152
|
-
"SessionStart": [
|
|
153
|
-
{
|
|
154
|
-
"hooks": [
|
|
155
|
-
{
|
|
156
|
-
"type": "command",
|
|
157
|
-
"command": "node \\"{{HOME}}/.grok/hooks/omo-memory-session.mjs\\"",
|
|
158
|
-
"timeout": 2,
|
|
159
|
-
"description": "omo-memory session bootstrap",
|
|
160
|
-
"statusMessage": "OMO Memory: loading recent session memory"
|
|
161
|
-
}
|
|
162
|
-
]
|
|
163
|
-
}
|
|
164
|
-
]
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
`;
|
package/dist/hooks.js
DELETED
|
@@ -1,198 +0,0 @@
|
|
|
1
|
-
import { spawnSync } from "node:child_process";
|
|
2
|
-
import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
|
-
import { homedir } from "node:os";
|
|
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_MCP_JSON, GROK_PLUGIN_JSON, GROK_SKILL, SESSION_BOOTSTRAP_SCRIPT, } from "./hookTemplates.js";
|
|
6
|
-
const BLOCK_START = "<!-- omo-memory:start -->";
|
|
7
|
-
const BLOCK_END = "<!-- omo-memory:end -->";
|
|
8
|
-
export function installHooks(input) {
|
|
9
|
-
const home = process.env["OMO_MEMORY_INSTALL_HOME"] ?? homedir();
|
|
10
|
-
return { home, installed: targetsFor(input.host).map((target) => installTarget(target, home)) };
|
|
11
|
-
}
|
|
12
|
-
function targetsFor(host) {
|
|
13
|
-
if (host === "all")
|
|
14
|
-
return ["codex", "grok"];
|
|
15
|
-
return [host];
|
|
16
|
-
}
|
|
17
|
-
function installTarget(host, home) {
|
|
18
|
-
if (host === "codex")
|
|
19
|
-
return installCodex(home);
|
|
20
|
-
return installGrok(home);
|
|
21
|
-
}
|
|
22
|
-
function installCodex(home) {
|
|
23
|
-
const skillPath = join(home, ".codex", "skills", "omo-memory", "SKILL.md");
|
|
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");
|
|
34
|
-
writeText(skillPath, CODEX_SKILL);
|
|
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);
|
|
45
|
-
return {
|
|
46
|
-
host: "codex",
|
|
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
|
-
],
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
|
-
function installGrok(home) {
|
|
57
|
-
const skillPath = join(home, ".grok", "skills", "omo-memory", "SKILL.md");
|
|
58
|
-
const agentsPath = join(home, ".grok", "AGENTS.md");
|
|
59
|
-
const hookScriptPath = join(home, ".grok", "hooks", "omo-memory-session.mjs");
|
|
60
|
-
const hookJsonPath = join(home, ".grok", "hooks", "omo-memory-hooks.json");
|
|
61
|
-
const pluginRoot = join(home, ".grok", "plugins", "omo-memory");
|
|
62
|
-
const pluginJsonPath = join(pluginRoot, "plugin.json");
|
|
63
|
-
const pluginSkillPath = join(pluginRoot, "skills", "omo-memory", "SKILL.md");
|
|
64
|
-
const pluginHookJsonPath = join(pluginRoot, "hooks", "hooks.json");
|
|
65
|
-
const pluginHookScriptPath = join(pluginRoot, "scripts", "omo-memory-session.mjs");
|
|
66
|
-
const pluginMcpPath = join(pluginRoot, ".mcp.json");
|
|
67
|
-
writeText(skillPath, GROK_SKILL);
|
|
68
|
-
upsertAgentsBlock(agentsPath, GROK_AGENTS_BLOCK);
|
|
69
|
-
writeText(hookScriptPath, GROK_HOOK_SCRIPT);
|
|
70
|
-
chmodSync(hookScriptPath, 0o755);
|
|
71
|
-
writeText(hookJsonPath, GROK_HOOKS_JSON.replace("{{HOME}}", home));
|
|
72
|
-
writeText(pluginJsonPath, GROK_PLUGIN_JSON);
|
|
73
|
-
writeText(pluginSkillPath, GROK_SKILL);
|
|
74
|
-
writeText(pluginHookJsonPath, GROK_HOOKS_JSON.replace(`node \\"{{HOME}}/.grok/hooks/omo-memory-session.mjs\\"`, `node \\"${pluginHookScriptPath}\\"`));
|
|
75
|
-
writeText(pluginHookScriptPath, GROK_HOOK_SCRIPT);
|
|
76
|
-
chmodSync(pluginHookScriptPath, 0o755);
|
|
77
|
-
writeText(pluginMcpPath, GROK_MCP_JSON);
|
|
78
|
-
const pluginInstallNote = maybeInstallGrokPlugin(home, pluginRoot);
|
|
79
|
-
return {
|
|
80
|
-
host: "grok",
|
|
81
|
-
files: [skillPath, agentsPath, hookScriptPath, hookJsonPath, pluginJsonPath, pluginSkillPath, pluginHookJsonPath, pluginHookScriptPath, pluginMcpPath],
|
|
82
|
-
notes: ["Grok plugin bundle installed at ~/.grok/plugins/omo-memory.", pluginInstallNote],
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
function writeText(path, text) {
|
|
86
|
-
mkdirSync(dirname(path), { recursive: true });
|
|
87
|
-
writeFileSync(path, text, "utf8");
|
|
88
|
-
}
|
|
89
|
-
function upsertMarketplace(path, input) {
|
|
90
|
-
const marketplace = readMarketplace(path, input.withInterface);
|
|
91
|
-
const plugins = marketplace.plugins.filter((plugin) => plugin.name !== CODEX_PLUGIN);
|
|
92
|
-
const omoMemory = marketplacePlugin(input.withInterface);
|
|
93
|
-
const next = { ...marketplace, plugins: [...plugins, omoMemory] };
|
|
94
|
-
writeText(path, `${JSON.stringify(next, null, 2)}\n`);
|
|
95
|
-
}
|
|
96
|
-
function readMarketplace(path, withInterface) {
|
|
97
|
-
if (!existsSync(path))
|
|
98
|
-
return defaultMarketplace(withInterface);
|
|
99
|
-
const parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
100
|
-
if (!isMarketplace(parsed))
|
|
101
|
-
return defaultMarketplace(withInterface);
|
|
102
|
-
return parsed;
|
|
103
|
-
}
|
|
104
|
-
function defaultMarketplace(withInterface) {
|
|
105
|
-
return {
|
|
106
|
-
name: CODEX_MARKETPLACE,
|
|
107
|
-
...(withInterface ? { interface: { displayName: CODEX_MARKETPLACE } } : {}),
|
|
108
|
-
plugins: [],
|
|
109
|
-
};
|
|
110
|
-
}
|
|
111
|
-
function marketplacePlugin(withPolicy) {
|
|
112
|
-
return {
|
|
113
|
-
name: CODEX_PLUGIN,
|
|
114
|
-
source: { source: "local", path: "./plugins/omo-memory" },
|
|
115
|
-
...(withPolicy ? { policy: { installation: "AVAILABLE", authentication: "ON_INSTALL" }, category: "Developer Tools" } : {}),
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
|
-
function isMarketplace(value) {
|
|
119
|
-
if (!isRecord(value) || value["name"] !== CODEX_MARKETPLACE || !Array.isArray(value["plugins"]))
|
|
120
|
-
return false;
|
|
121
|
-
return value["plugins"].every(isMarketplacePlugin);
|
|
122
|
-
}
|
|
123
|
-
function isMarketplacePlugin(value) {
|
|
124
|
-
if (!isRecord(value) || typeof value["name"] !== "string" || !isRecord(value["source"]))
|
|
125
|
-
return false;
|
|
126
|
-
const source = value["source"];
|
|
127
|
-
return source["source"] === "local" && typeof source["path"] === "string";
|
|
128
|
-
}
|
|
129
|
-
function isRecord(value) {
|
|
130
|
-
return typeof value === "object" && value !== null;
|
|
131
|
-
}
|
|
132
|
-
function upsertCodexConfig(path, marketplacePath) {
|
|
133
|
-
const existing = existsSync(path) ? readFileSync(path, "utf8") : "";
|
|
134
|
-
const withHooks = ensureTomlKey(existing, "[features]", "plugin_hooks = true");
|
|
135
|
-
const withMarketplace = ensureTomlTable(withHooks, `[marketplaces.${CODEX_MARKETPLACE}]`, `source_type = "local"\nsource = "${escapeTomlString(marketplacePath)}"`);
|
|
136
|
-
writeText(path, ensureTomlTable(withMarketplace, `[plugins."${CODEX_PLUGIN}@${CODEX_MARKETPLACE}"]`, "enabled = true"));
|
|
137
|
-
}
|
|
138
|
-
function maybeInstallCodexPlugin(home) {
|
|
139
|
-
if (home !== homedir())
|
|
140
|
-
return "Skipped `codex plugin add` because OMO_MEMORY_INSTALL_HOME points at a test/alternate home.";
|
|
141
|
-
const result = spawnSync("codex", ["plugin", "add", `${CODEX_PLUGIN}@${CODEX_MARKETPLACE}`, "--json"], {
|
|
142
|
-
encoding: "utf8",
|
|
143
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
144
|
-
timeout: 15000,
|
|
145
|
-
});
|
|
146
|
-
if (result.status === 0)
|
|
147
|
-
return "`codex plugin add omo-memory@islee23520 --json` completed successfully.";
|
|
148
|
-
const detail = result.error instanceof Error ? result.error.message : result.stderr.trim();
|
|
149
|
-
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.`;
|
|
150
|
-
}
|
|
151
|
-
function maybeInstallGrokPlugin(home, pluginRoot) {
|
|
152
|
-
if (home !== homedir())
|
|
153
|
-
return "Skipped `grok plugin install` because OMO_MEMORY_INSTALL_HOME points at a test/alternate home.";
|
|
154
|
-
const result = spawnSync("grok", ["plugin", "install", pluginRoot, "--trust"], {
|
|
155
|
-
encoding: "utf8",
|
|
156
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
157
|
-
timeout: 15000,
|
|
158
|
-
});
|
|
159
|
-
if (result.status === 0)
|
|
160
|
-
return "`grok plugin install ~/.grok/plugins/omo-memory --trust` completed successfully.";
|
|
161
|
-
const detail = result.error instanceof Error ? result.error.message : result.stderr.trim();
|
|
162
|
-
return `Could not run \`grok plugin install ~/.grok/plugins/omo-memory --trust\`: ${detail || "unknown error"}. Run it manually if Grok still reports the plugin as not installed.`;
|
|
163
|
-
}
|
|
164
|
-
function ensureTomlKey(text, table, keyValue) {
|
|
165
|
-
if (text.includes(keyValue))
|
|
166
|
-
return text;
|
|
167
|
-
if (!text.includes(table))
|
|
168
|
-
return appendTomlTable(text, table, keyValue);
|
|
169
|
-
const start = text.indexOf(table);
|
|
170
|
-
const nextTable = text.indexOf("\n[", start + table.length);
|
|
171
|
-
const insertAt = nextTable === -1 ? text.length : nextTable;
|
|
172
|
-
return `${text.slice(0, insertAt).trimEnd()}\n${keyValue}\n${text.slice(insertAt).trimStart()}`;
|
|
173
|
-
}
|
|
174
|
-
function ensureTomlTable(text, table, body) {
|
|
175
|
-
if (text.includes(table))
|
|
176
|
-
return text;
|
|
177
|
-
return appendTomlTable(text, table, body);
|
|
178
|
-
}
|
|
179
|
-
function appendTomlTable(text, table, body) {
|
|
180
|
-
const prefix = text.trimEnd();
|
|
181
|
-
const separator = prefix.length === 0 ? "" : "\n\n";
|
|
182
|
-
return `${prefix}${separator}${table}\n${body}\n`;
|
|
183
|
-
}
|
|
184
|
-
function escapeTomlString(value) {
|
|
185
|
-
return value.replaceAll("\\", "\\\\").replaceAll('"', '\\"');
|
|
186
|
-
}
|
|
187
|
-
function upsertAgentsBlock(path, block) {
|
|
188
|
-
const existing = existsSync(path) ? readFileSync(path, "utf8") : "";
|
|
189
|
-
const wrapped = `${BLOCK_START}\n${block}\n${BLOCK_END}`;
|
|
190
|
-
const start = existing.indexOf(BLOCK_START);
|
|
191
|
-
const end = existing.indexOf(BLOCK_END);
|
|
192
|
-
if (start !== -1 && end > start) {
|
|
193
|
-
writeText(path, `${existing.slice(0, start)}${wrapped}${existing.slice(end + BLOCK_END.length)}`);
|
|
194
|
-
return;
|
|
195
|
-
}
|
|
196
|
-
const separator = existing.trim().length === 0 ? "" : "\n\n";
|
|
197
|
-
writeText(path, `${existing.trimEnd()}${separator}${wrapped}\n`);
|
|
198
|
-
}
|