From 346cef2488d2b50c448f39f3e6ff1b04d907d695 Mon Sep 17 00:00:00 2001 From: Alex Newman Date: Wed, 28 Jan 2026 14:04:28 -0500 Subject: [PATCH] feat(memory-claudemem): scaffold plugin structure Create extensions/memory-claudemem with: - package.json with workspace references - clawdbot.plugin.json manifest - types.ts, config.ts, client.ts (stubs), index.ts Part of claude-mem integration (Phase 2/7). Co-Authored-By: Claude Opus 4.5 --- .../memory-claudemem/clawdbot.plugin.json | 27 ++++++++++ extensions/memory-claudemem/client.ts | 32 ++++++++++++ extensions/memory-claudemem/config.ts | 49 +++++++++++++++++++ extensions/memory-claudemem/index.ts | 26 ++++++++++ extensions/memory-claudemem/package.json | 13 +++++ extensions/memory-claudemem/types.ts | 24 +++++++++ 6 files changed, 171 insertions(+) create mode 100644 extensions/memory-claudemem/clawdbot.plugin.json create mode 100644 extensions/memory-claudemem/client.ts create mode 100644 extensions/memory-claudemem/config.ts create mode 100644 extensions/memory-claudemem/index.ts create mode 100644 extensions/memory-claudemem/package.json create mode 100644 extensions/memory-claudemem/types.ts diff --git a/extensions/memory-claudemem/clawdbot.plugin.json b/extensions/memory-claudemem/clawdbot.plugin.json new file mode 100644 index 000000000..5149fd97a --- /dev/null +++ b/extensions/memory-claudemem/clawdbot.plugin.json @@ -0,0 +1,27 @@ +{ + "id": "memory-claudemem", + "name": "Memory (Claude-Mem)", + "description": "Real-time observation and memory via claude-mem worker", + "kind": "memory", + "version": "0.0.1", + "uiHints": { + "workerUrl": { + "label": "Worker URL", + "placeholder": "http://localhost:37777", + "help": "URL of the claude-mem worker" + }, + "workerTimeout": { + "label": "Timeout (ms)", + "placeholder": "10000", + "advanced": true + } + }, + "configSchema": { + "type": "object", + "additionalProperties": false, + "properties": { + "workerUrl": { "type": "string", "default": "http://localhost:37777" }, + "workerTimeout": { "type": "number", "default": 10000 } + } + } +} diff --git a/extensions/memory-claudemem/client.ts b/extensions/memory-claudemem/client.ts new file mode 100644 index 000000000..0d7f76d91 --- /dev/null +++ b/extensions/memory-claudemem/client.ts @@ -0,0 +1,32 @@ +import type { SearchResult, Observation } from "./types.js"; + +export class ClaudeMemClient { + constructor( + private readonly baseUrl: string, + private readonly timeout: number, + ) {} + + async ping(): Promise { + // TODO: Phase 3 + return false; + } + + async observe( + sessionId: string, + toolName: string, + toolInput: unknown, + toolResponse: unknown, + ): Promise { + // TODO: Phase 3 + } + + async search(query: string, limit = 10): Promise { + // TODO: Phase 3 + return []; + } + + async getObservations(ids: number[]): Promise { + // TODO: Phase 3 + return []; + } +} diff --git a/extensions/memory-claudemem/config.ts b/extensions/memory-claudemem/config.ts new file mode 100644 index 000000000..b81167113 --- /dev/null +++ b/extensions/memory-claudemem/config.ts @@ -0,0 +1,49 @@ +export type ClaudeMemConfig = { + workerUrl: string; + workerTimeout: number; +}; + +const DEFAULT_WORKER_URL = "http://localhost:37777"; +const DEFAULT_TIMEOUT = 10000; + +function assertAllowedKeys( + value: Record, + allowed: string[], + label: string, +) { + const unknown = Object.keys(value).filter((key) => !allowed.includes(key)); + if (unknown.length === 0) return; + throw new Error(`${label} has unknown keys: ${unknown.join(", ")}`); +} + +export const claudeMemConfigSchema = { + parse(value: unknown): ClaudeMemConfig { + if (!value || typeof value !== "object" || Array.isArray(value)) { + // Return defaults if no config provided + return { + workerUrl: DEFAULT_WORKER_URL, + workerTimeout: DEFAULT_TIMEOUT, + }; + } + const cfg = value as Record; + assertAllowedKeys(cfg, ["workerUrl", "workerTimeout"], "claude-mem config"); + + return { + workerUrl: typeof cfg.workerUrl === "string" ? cfg.workerUrl : DEFAULT_WORKER_URL, + workerTimeout: typeof cfg.workerTimeout === "number" ? cfg.workerTimeout : DEFAULT_TIMEOUT, + }; + }, + uiHints: { + workerUrl: { + label: "Worker URL", + placeholder: DEFAULT_WORKER_URL, + help: "URL of the claude-mem worker", + }, + workerTimeout: { + label: "Timeout (ms)", + placeholder: String(DEFAULT_TIMEOUT), + advanced: true, + help: "Request timeout in milliseconds", + }, + }, +}; diff --git a/extensions/memory-claudemem/index.ts b/extensions/memory-claudemem/index.ts new file mode 100644 index 000000000..10753d507 --- /dev/null +++ b/extensions/memory-claudemem/index.ts @@ -0,0 +1,26 @@ +import type { ClawdbotPluginApi } from "clawdbot/plugin-sdk"; +import { claudeMemConfigSchema } from "./config.js"; +import { ClaudeMemClient } from "./client.js"; + +const claudeMemPlugin = { + id: "memory-claudemem", + name: "Memory (Claude-Mem)", + description: "Real-time observation and memory via claude-mem worker", + kind: "memory" as const, + configSchema: claudeMemConfigSchema, + + register(api: ClawdbotPluginApi) { + const cfg = claudeMemConfigSchema.parse(api.pluginConfig); + const client = new ClaudeMemClient(cfg.workerUrl, cfg.workerTimeout); + + api.logger.info( + `memory-claudemem: plugin registered (worker: ${cfg.workerUrl})`, + ); + + // TODO: Phase 4 - Hook registration + // TODO: Phase 5 - Tool registration + // TODO: Phase 6 - CLI registration + }, +}; + +export default claudeMemPlugin; diff --git a/extensions/memory-claudemem/package.json b/extensions/memory-claudemem/package.json new file mode 100644 index 000000000..1252813b3 --- /dev/null +++ b/extensions/memory-claudemem/package.json @@ -0,0 +1,13 @@ +{ + "name": "@clawdbot/memory-claudemem", + "version": "0.0.1", + "type": "module", + "main": "index.ts", + "dependencies": {}, + "devDependencies": { + "clawdbot": "workspace:*" + }, + "peerDependencies": { + "clawdbot": "*" + } +} diff --git a/extensions/memory-claudemem/types.ts b/extensions/memory-claudemem/types.ts new file mode 100644 index 000000000..0a0833ae7 --- /dev/null +++ b/extensions/memory-claudemem/types.ts @@ -0,0 +1,24 @@ +/** Search result from claude-mem worker (index format) */ +export type SearchResult = { + id: number; + title: string; + snippet?: string; + score?: number; +}; + +/** Full observation from claude-mem worker */ +export type Observation = { + id: number; + narrative: string; + files_modified?: string[]; + tool_name?: string; + tool_input?: unknown; + tool_response?: unknown; + created_at_epoch?: number; +}; + +/** Plugin configuration */ +export type ClaudeMemConfig = { + workerUrl: string; + workerTimeout: number; +};