nixos/shared/linked-dotfiles/opencode/plugin/llmemory.js
2025-10-29 18:46:16 -06:00

148 lines
4.8 KiB
JavaScript

/**
* LLMemory Plugin for OpenCode
*
* Provides a persistent memory/journal system for AI agents.
* Memories are stored in SQLite and searchable across sessions.
*/
import { tool } from "@opencode-ai/plugin";
import { spawn } from "child_process";
import { fileURLToPath } from 'url';
import { dirname, join } from 'path';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const MEMORY_CLI = join(__dirname, '../llmemory/bin/llmemory');
function runMemoryCommand(args) {
return new Promise((resolve, reject) => {
const child = spawn('node', [MEMORY_CLI, ...args], {
env: { ...process.env }
});
let stdout = '';
let stderr = '';
child.stdout.on('data', (data) => {
stdout += data.toString();
});
child.stderr.on('data', (data) => {
stderr += data.toString();
});
child.on('close', (code) => {
if (code !== 0) {
reject(new Error(stderr || `Command failed with code ${code}`));
} else {
resolve(stdout);
}
});
});
}
export const LLMemoryPlugin = async (ctx) => {
const tools = {
memory_store: tool({
description: `Store a memory for future reference. Use this to remember important information across sessions.
Examples:
- Store implementation decisions: "Decided to use JWT for auth instead of sessions"
- Record completed work: "Implemented user authentication with email/password"
- Save debugging insights: "Bug was caused by race condition in async handler"
- Document project context: "Client prefers Material-UI over Tailwind"
Memories are searchable by content and tags.`,
args: {
content: tool.schema.string()
.describe("The memory content to store (required)"),
tags: tool.schema.string()
.optional()
.describe("Comma-separated tags for categorization (e.g., 'backend,auth,security')"),
expires: tool.schema.string()
.optional()
.describe("Optional expiration date (ISO format, e.g., '2026-12-31')"),
by: tool.schema.string()
.optional()
.describe("Agent/user identifier (defaults to 'agent')")
},
async execute(args) {
const cmdArgs = ['store', args.content];
if (args.tags) cmdArgs.push('--tags', args.tags);
if (args.expires) cmdArgs.push('--expires', args.expires);
if (args.by) cmdArgs.push('--by', args.by);
try {
const result = await runMemoryCommand(cmdArgs);
return result;
} catch (error) {
return `Error storing memory: ${error.message}`;
}
}
}),
memory_search: tool({
description: `Search stored memories by content and/or tags. Returns relevant memories from past sessions.
Use cases:
- Find past decisions: "authentication"
- Recall debugging insights: "race condition"
- Look up project context: "client preferences"
- Review completed work: "implemented"
Supports filtering by tags, date ranges, and limiting results.`,
args: {
query: tool.schema.string()
.describe("Search query (case-insensitive substring match)"),
tags: tool.schema.string()
.optional()
.describe("Filter by tags (AND logic, comma-separated)"),
any_tag: tool.schema.string()
.optional()
.describe("Filter by tags (OR logic, comma-separated)"),
limit: tool.schema.number()
.optional()
.describe("Maximum results to return (default: 10)")
},
async execute(args) {
const cmdArgs = ['search', args.query, '--json'];
if (args.tags) cmdArgs.push('--tags', args.tags);
if (args.any_tag) cmdArgs.push('--any-tag', args.any_tag);
if (args.limit) cmdArgs.push('--limit', String(args.limit));
try {
const result = await runMemoryCommand(cmdArgs);
return result;
} catch (error) {
return `Error searching memories: ${error.message}`;
}
}
}),
memory_list: tool({
description: `List recent memories, optionally filtered by tags. Useful for reviewing recent work or exploring stored context.`,
args: {
limit: tool.schema.number()
.optional()
.describe("Maximum results to return (default: 20)"),
tags: tool.schema.string()
.optional()
.describe("Filter by tags (comma-separated)")
},
async execute(args) {
const cmdArgs = ['list', '--json'];
if (args.limit) cmdArgs.push('--limit', String(args.limit));
if (args.tags) cmdArgs.push('--tags', args.tags);
try {
const result = await runMemoryCommand(cmdArgs);
return result;
} catch (error) {
return `Error listing memories: ${error.message}`;
}
}
})
};
return { tool: tools };
};