Skip to content

Instantly share code, notes, and snippets.

@mariodian
Created April 11, 2026 14:58
Show Gist options
  • Select an option

  • Save mariodian/69bca1c1a80d40d60b7833ccbc19466d to your computer and use it in GitHub Desktop.

Select an option

Save mariodian/69bca1c1a80d40d60b7833ccbc19466d to your computer and use it in GitHub Desktop.
Mempalace Auto Save Plugin for OpenCode
/*
- Mempalace Auto Save Plugin for OpenCode
-
- Installation:
- Manually copy this file to ~/.config/opencode/plugins/
*/
import type { Hooks, Plugin } from "@opencode-ai/plugin";
import type { Event } from "@opencode-ai/sdk";
const SAVE_INTERVAL = 5; // Save every N user messages
interface TranscriptEntry {
role: "user" | "assistant";
text: string;
timestamp: number;
}
export const MempalaceSave: Plugin = async ({
$,
directory,
}): Promise<Hooks> => {
let userMessageCount = 0;
let entries: TranscriptEntry[] = [];
let saving = false;
const messageRoles = new Map<string, "user" | "assistant">();
const seenMessageIds = new Set<string>();
async function flush(): Promise<void> {
if (entries.length === 0 || saving) return;
saving = true;
const transcript = entries.map((e) => `[${e.role}] ${e.text}`).join("\n\n");
const tmpFile = `${directory}/.mempalace_autosave_${Date.now()}.txt`;
try {
const { writeFileSync } = await import("node:fs");
writeFileSync(tmpFile, transcript, "utf-8");
await $`bash -c 'mempalace mine ${tmpFile} --mode convos >/dev/null 2>&1'`;
await $`rm -f ${tmpFile}`;
} catch {
try {
await $`rm -f ${tmpFile}`.nothrow();
} catch {
// ignore cleanup errors
}
}
entries = [];
userMessageCount = 0;
saving = false;
}
return {
event: async ({ event }: { event: Event }): Promise<void> => {
if (event.type === "message.updated") {
const info = event.properties.info as
| { id: string; role: "user" | "assistant" }
| undefined;
if (info?.id && info?.role) {
messageRoles.set(info.id, info.role);
}
}
if (event.type === "message.part.updated") {
const part = event.properties.part as {
messageID?: string;
type: string;
text?: string;
};
const messageId = part.messageID ?? "";
if (!messageId) return;
if (seenMessageIds.has(messageId)) return;
if (part.type === "text" && part.text?.trim()) {
const role = messageRoles.get(messageId);
if (role) {
seenMessageIds.add(messageId);
entries.push({ role, text: part.text, timestamp: Date.now() });
if (role === "user") {
userMessageCount++;
if (userMessageCount >= SAVE_INTERVAL) {
await flush();
}
}
}
}
}
if (event.type === "session.end" && entries.length > 0) {
await flush();
}
},
};
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment