opencode-memelord 0.0.1

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 ADDED
@@ -0,0 +1,74 @@
1
+ # opencode-memelord
2
+
3
+ [OpenCode](https://opencode.ai) plugin for [memelord](https://github.com/glommer/memelord) -- persistent memory for coding agents.
4
+
5
+ ## What it does
6
+
7
+ Gives your OpenCode agent persistent memory that improves over time. Memories from past sessions are injected at session start, tool failures are tracked, and transcripts are analyzed for self-corrections and discoveries.
8
+
9
+ | OpenCode event | memelord hook | Purpose |
10
+ |---|---|---|
11
+ | `session.created` | `session-start` | Inject top memories into context |
12
+ | `tool.execute.after` | `post-tool-use` | Record tool failures for pattern detection |
13
+ | `session.idle` | `stop` | Analyze transcript for corrections and discoveries |
14
+ | `session.deleted` | `session-end` | Embed pending memories, run weight decay |
15
+
16
+ ## Install
17
+
18
+ ### Quick start (with memelord init)
19
+
20
+ ```bash
21
+ npm install -g memelord
22
+ cd your-project
23
+ memelord init
24
+ ```
25
+
26
+ This configures both the MCP server (for memory tools) and the plugin (for hooks).
27
+
28
+ ### Manual setup
29
+
30
+ Add to your `opencode.json`:
31
+
32
+ ```json
33
+ {
34
+ "plugin": ["opencode-memelord"],
35
+ "mcp": {
36
+ "memelord": {
37
+ "type": "local",
38
+ "command": ["memelord", "serve"],
39
+ "environment": { "MEMELORD_DIR": ".memelord" },
40
+ "enabled": true
41
+ }
42
+ }
43
+ }
44
+ ```
45
+
46
+ Make sure memelord is installed globally:
47
+
48
+ ```bash
49
+ npm install -g memelord
50
+ ```
51
+
52
+ Then initialize the project database:
53
+
54
+ ```bash
55
+ memelord init
56
+ ```
57
+
58
+ ## How it works
59
+
60
+ This plugin is a thin wrapper around the `memelord hook` CLI. It translates OpenCode's plugin events into the same hook commands that memelord uses for Claude Code, so all the analysis, storage, embedding, and decay logic stays in memelord.
61
+
62
+ - **No logic is duplicated** -- the plugin delegates everything to the memelord CLI
63
+ - **Automatic upstream updates** -- when memelord improves its hooks, this plugin benefits without changes
64
+ - **MCP tools still work** -- the plugin handles lifecycle hooks, the MCP server provides memory tools (`memory_start_task`, `memory_report`, `memory_end_task`, `memory_contradict`, `memory_status`)
65
+
66
+ ## Requirements
67
+
68
+ - [memelord](https://github.com/glommer/memelord) installed globally (`npm install -g memelord`)
69
+ - [OpenCode](https://opencode.ai) v1.0+
70
+ - A `.memelord/` directory in your project (created by `memelord init`)
71
+
72
+ ## License
73
+
74
+ MIT
@@ -0,0 +1,15 @@
1
+ /**
2
+ * opencode-memelord: OpenCode plugin for memelord persistent memory.
3
+ *
4
+ * Thin wrapper that delegates to `memelord hook <event>` CLI commands.
5
+ * No business logic is ported — all analysis, storage, embedding, and
6
+ * decay happens in the memelord CLI.
7
+ *
8
+ * Hook mapping:
9
+ * session.created → memelord hook session-start
10
+ * tool.execute.after → memelord hook post-tool-use
11
+ * session.idle → memelord hook stop
12
+ * session.deleted → memelord hook session-end
13
+ */
14
+ import type { Plugin } from "@opencode-ai/plugin";
15
+ export declare const MemelordPlugin: Plugin;
package/dist/index.js ADDED
@@ -0,0 +1,154 @@
1
+ // src/index.ts
2
+ import { dirname, join } from "path";
3
+ import { createRequire } from "module";
4
+ import { writeFileSync, unlinkSync, existsSync } from "fs";
5
+ import { tmpdir } from "os";
6
+ function resolveMemelordBin() {
7
+ try {
8
+ const require2 = createRequire(import.meta.url);
9
+ const pkgPath = require2.resolve("memelord/package.json");
10
+ return join(dirname(pkgPath), "dist", "cli.mjs");
11
+ } catch {
12
+ return "";
13
+ }
14
+ }
15
+ var MemelordPlugin = async ({ client, directory, worktree, $ }) => {
16
+ const cwd = worktree || directory;
17
+ const memelordBin = resolveMemelordBin();
18
+ let currentSessionId = "";
19
+ async function callHook(event, stdinData) {
20
+ const json = JSON.stringify(stdinData);
21
+ try {
22
+ if (memelordBin) {
23
+ return await $`echo ${json} | node ${memelordBin} hook ${event}`.quiet().nothrow().text();
24
+ }
25
+ return await $`echo ${json} | memelord hook ${event}`.quiet().nothrow().text();
26
+ } catch {
27
+ return "";
28
+ }
29
+ }
30
+ function convertTranscript(messages) {
31
+ const lines = [];
32
+ for (const { info, parts } of messages) {
33
+ const content = [];
34
+ for (const part of parts) {
35
+ if (part.type === "text") {
36
+ content.push({ type: "text", text: part.text });
37
+ } else if (part.type === "tool") {
38
+ content.push({
39
+ type: "tool_use",
40
+ name: part.tool,
41
+ input: part.state.status !== "pending" ? part.state.input : {}
42
+ });
43
+ if (part.state.status === "completed") {
44
+ content.push({ type: "tool_result", content: part.state.output });
45
+ } else if (part.state.status === "error") {
46
+ content.push({
47
+ type: "tool_result",
48
+ content: part.state.error,
49
+ is_error: true
50
+ });
51
+ }
52
+ }
53
+ }
54
+ const msg = { role: info.role, content };
55
+ if (info.role === "assistant") {
56
+ const a = info;
57
+ msg.usage = {
58
+ input_tokens: a.tokens.input,
59
+ output_tokens: a.tokens.output,
60
+ cache_creation_input_tokens: a.tokens.cache.write
61
+ };
62
+ }
63
+ lines.push(JSON.stringify(msg));
64
+ }
65
+ return lines.join(`
66
+ `);
67
+ }
68
+ async function onSessionCreated(sessionId) {
69
+ currentSessionId = sessionId;
70
+ if (!existsSync(join(cwd, ".memelord")))
71
+ return;
72
+ const stdout = await callHook("session-start", {
73
+ session_id: sessionId,
74
+ cwd
75
+ });
76
+ if (!stdout.trim())
77
+ return;
78
+ try {
79
+ const parsed = JSON.parse(stdout);
80
+ const context = parsed?.hookSpecificOutput?.additionalContext;
81
+ if (context) {
82
+ await client.session.prompt({
83
+ path: { id: sessionId },
84
+ body: {
85
+ noReply: true,
86
+ parts: [{ type: "text", text: context }]
87
+ }
88
+ });
89
+ }
90
+ } catch {}
91
+ }
92
+ async function onSessionIdle(sessionId) {
93
+ if (!existsSync(join(cwd, ".memelord")))
94
+ return;
95
+ const stdinData = {
96
+ session_id: sessionId,
97
+ cwd
98
+ };
99
+ try {
100
+ const response = await client.session.messages({
101
+ path: { id: sessionId }
102
+ });
103
+ const messages = response.data;
104
+ if (messages && messages.length > 0) {
105
+ const transcript = convertTranscript(messages);
106
+ const tmpFile = join(tmpdir(), `memelord-transcript-${sessionId}.jsonl`);
107
+ writeFileSync(tmpFile, transcript);
108
+ stdinData.transcript_path = tmpFile;
109
+ await callHook("stop", stdinData);
110
+ try {
111
+ unlinkSync(tmpFile);
112
+ } catch {}
113
+ return;
114
+ }
115
+ } catch {}
116
+ await callHook("stop", stdinData);
117
+ }
118
+ async function onSessionDeleted(sessionId) {
119
+ if (!existsSync(join(cwd, ".memelord")))
120
+ return;
121
+ await callHook("session-end", {
122
+ session_id: sessionId,
123
+ cwd
124
+ });
125
+ }
126
+ return {
127
+ "tool.execute.after": async (input, output) => {
128
+ if (!existsSync(join(cwd, ".memelord")))
129
+ return;
130
+ if (!currentSessionId)
131
+ return;
132
+ callHook("post-tool-use", {
133
+ session_id: currentSessionId,
134
+ cwd,
135
+ tool_name: input.tool,
136
+ tool_input: input.args,
137
+ tool_response: output.output ?? ""
138
+ }).catch(() => {});
139
+ },
140
+ event: async (input) => {
141
+ const { event } = input;
142
+ if (event.type === "session.created") {
143
+ await onSessionCreated(event.properties.info.id);
144
+ } else if (event.type === "session.idle") {
145
+ onSessionIdle(event.properties.sessionID).catch(() => {});
146
+ } else if (event.type === "session.deleted") {
147
+ await onSessionDeleted(event.properties.info.id);
148
+ }
149
+ }
150
+ };
151
+ };
152
+ export {
153
+ MemelordPlugin
154
+ };
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "opencode-memelord",
3
+ "version": "0.0.1",
4
+ "description": "OpenCode plugin for memelord - persistent memory for coding agents",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "scripts": {
9
+ "build": "bun build ./src/index.ts --outdir ./dist --target node && tsc --emitDeclarationOnly",
10
+ "typecheck": "tsc --noEmit"
11
+ },
12
+ "keywords": [
13
+ "opencode",
14
+ "plugin",
15
+ "memelord",
16
+ "memory",
17
+ "ai",
18
+ "coding-agent"
19
+ ],
20
+ "license": "MIT",
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "https://github.com/buzinas/opencode-memelord"
24
+ },
25
+ "dependencies": {
26
+ "@opencode-ai/plugin": "^1.2.10",
27
+ "memelord": "^0.1.4"
28
+ },
29
+ "devDependencies": {
30
+ "@opencode-ai/sdk": "^1.2.10",
31
+ "@types/bun": "latest",
32
+ "typescript": "^5.0.0"
33
+ },
34
+ "opencode": {
35
+ "type": "plugin",
36
+ "hooks": [
37
+ "event",
38
+ "tool.execute.after"
39
+ ]
40
+ },
41
+ "files": [
42
+ "dist"
43
+ ]
44
+ }