open-memex
Version:
Local-first memory layer and protocol for AI coding agents. Markdown source of truth, SQLite FTS5 index, zero cloud.
44 lines (43 loc) • 2.07 kB
JavaScript
import { PLUGIN_ID, bootstrapPlugin, captureFromText, captureHandoff, injectOnce, } from "./plugin-core.js";
import { pickScopeRoot } from "./scope.js";
import { makeTools } from "./tools/memory.js";
import { v2Plugin } from "./opencode-v2.js";
/**
* One entrypoint, both opencode generations (D60):
* - OpenCode 2 structurally decodes the default export as `{ id, setup }`
* and ignores `server` (see src/opencode-v2.ts).
* - OpenCode 1 (≥1.18.29) reads `server` off the same default export and
* ignores `setup` — the function below is the v1 plugin exactly as it
* behaved before dual support.
* Neither host runtime-imports an SDK package from this file: v1's SDK is
* type-only (D58), and v2's decode is structural.
*/
const server = async ({ worktree, directory }) => {
// v1's `worktree` is the filesystem root for non-git folders — never seed a
// scope on it (D71). `pickScopeRoot` falls back to the opened `directory`.
const state = bootstrapPlugin(pickScopeRoot(worktree, directory));
const tools = makeTools(() => state.scope, state.cfg);
return {
tool: tools,
async "chat.message"(input, output) {
const parts = (output.parts ?? []);
const text = parts
.map((p) => (p?.type === "text" ? p.text ?? "" : ""))
.filter(Boolean)
.join("\n");
captureFromText(text, state, input.sessionID ?? "");
},
async "experimental.chat.system.transform"(input, output) {
const sessionID = input.sessionID ?? "";
injectOnce(sessionID, state, (block) => {
output.system.push(block);
});
// D73: runs on every request, so the note lands on the very turn whose
// message the hook just captured. Delivered once per capture.
const handoff = captureHandoff(sessionID, state);
if (handoff)
output.system.push(handoff);
},
};
};
export default { id: PLUGIN_ID, setup: v2Plugin.setup, server };