diff --git a/docs/plans/claude-mem-moltbot-fix.md b/docs/plans/claude-mem-moltbot-fix.md
new file mode 100644
index 000000000..117224beb
--- /dev/null
+++ b/docs/plans/claude-mem-moltbot-fix.md
@@ -0,0 +1,109 @@
+# Claude-Mem Moltbot Plugin Fix Plan
+
+## Status Assessment
+
+**Current State:**
+- Plugin loads from: `/Users/alexnewman/clawd/plugins/claude-mem/index.ts`
+- Observations: Working correctly (recorded via `tool_result_persist` hook)
+- Prompts: Being recorded BUT with `[message_id: ...]` suffix appended
+- MEMORY.md sync: Working (on `session_start`)
+- Context injection: NOT implemented (only MEMORY.md file approach)
+
+**Root Cause:**
+Moltbot's messaging system adds `[message_id: UUID]` to prompts for WhatsApp/Discord reactions and replies (see `src/auto-reply/reply/body.ts:42`). This metadata is being passed through to claude-mem without stripping.
+
+## Phase 1: Strip message_id from prompts
+
+### Problem
+Line 304 in `/Users/alexnewman/clawd/plugins/claude-mem/index.ts`:
+```typescript
+const result = await client.initSession(contentSessionId, event.prompt);
+```
+Sends the full prompt including `[message_id: UUID]`.
+
+### Fix
+Add a utility function to strip the message_id line before sending:
+
+**Copy pattern from:** `src/gateway/chat-sanitize.ts:18-38`
+
+```typescript
+const MESSAGE_ID_LINE = /^\s*\[message_id:\s*[^\]]+\]\s*$/im;
+
+function stripMessageIdFromPrompt(text: string): string {
+ if (!text.includes("[message_id:")) return text;
+ const lines = text.split(/\r?\n/);
+ const filtered = lines.filter((line) => !MESSAGE_ID_LINE.test(line));
+ return filtered.join("\n").trim();
+}
+```
+
+### Implementation
+Update `before_agent_start` hook (around line 297-310):
+
+```typescript
+api.on("before_agent_start", async (event, ctx) => {
+ if (!event.prompt || event.prompt.length < 10) return;
+ if (!(await client.isHealthy())) return;
+
+ const contentSessionId = getContentSessionId(ctx.sessionKey);
+
+ // Strip [message_id: ...] metadata before recording
+ const cleanPrompt = stripMessageIdFromPrompt(event.prompt);
+
+ const result = await client.initSession(contentSessionId, cleanPrompt);
+ if (result) {
+ api.logger.debug?.(
+ `claude-mem: session initialized (dbId: ${result.sessionDbId}, prompt#: ${result.promptNumber})`
+ );
+ }
+});
+```
+
+### Verification
+1. Run moltbot with a test message
+2. Check `/api/prompts` endpoint - prompts should NOT contain `[message_id: ...]`
+3. Check claude-mem UI - prompts should display cleanly
+
+---
+
+## Phase 2: (Optional) Add context injection via prependContext
+
+The current plugin only syncs to `MEMORY.md` file. For real-time context injection (like the `extensions/memory-claudemem` approach), add:
+
+```typescript
+api.on("before_agent_start", async (event, ctx) => {
+ // ... existing prompt recording code ...
+
+ // Optional: inject recent context
+ const observations = await client.getObservations(5);
+ if (observations.length === 0) return;
+
+ const memoryContext = observations
+ .map((obs) => `- [#${obs.id}] ${obs.title || obs.type}: ${obs.narrative || obs.text?.slice(0, 100)}`)
+ .join("\n");
+
+ return {
+ prependContext: `\nRecent memories:\n${memoryContext}\n`,
+ };
+});
+```
+
+**Note:** This is optional since MEMORY.md file approach is already working.
+
+---
+
+## Files to Modify
+
+| File | Change |
+|------|--------|
+| `/Users/alexnewman/clawd/plugins/claude-mem/index.ts` | Add `stripMessageIdFromPrompt()` utility, update `before_agent_start` hook |
+
+---
+
+## Verification Checklist
+
+- [ ] Prompts recorded without `[message_id: ...]` suffix
+- [ ] Prompts display cleanly in claude-mem UI at localhost:37777
+- [ ] Observations still being recorded correctly
+- [ ] MEMORY.md still syncing on session_start
+- [ ] No regressions in existing functionality
diff --git a/docs/plugins/memory-claudemem.md b/docs/plugins/memory-claudemem.md
index 018ee98e6..ce822a99a 100644
--- a/docs/plugins/memory-claudemem.md
+++ b/docs/plugins/memory-claudemem.md
@@ -60,25 +60,7 @@ bun run build
npm run build
```
-#### 4. Create the plugin cache directory
-
-The Moltbot plugin expects claude-mem to be in the Claude plugins cache:
-
-```bash
-# Create the directory structure
-mkdir -p ~/.claude/plugins/cache/thedotmack/claude-mem
-
-# Copy the built plugin (use actual version number)
-cp -r plugin ~/.claude/plugins/cache/thedotmack/claude-mem/9.0.12
-```
-
-Alternatively, symlink it:
-
-```bash
-ln -s $(pwd)/plugin ~/.claude/plugins/cache/thedotmack/claude-mem/9.0.12
-```
-
-#### 5. Start the worker service
+#### 4. Start the worker service
Claude-mem runs a background worker service:
@@ -100,6 +82,43 @@ The worker runs on `http://localhost:37777` by default and provides:
- API endpoints for memory search and retrieval
- Background observation processing
+#### 5. Configure Moltbot with the worker path
+
+Tell Moltbot where to find the worker service:
+
+```json5
+{
+ plugins: {
+ entries: {
+ "memory-claudemem": {
+ enabled: true,
+ config: {
+ workerPath: "/path/to/claude-mem/plugin/scripts/worker-service.cjs"
+ }
+ }
+ }
+ }
+}
+```
+
+Or set the slot directly:
+
+```json5
+{
+ plugins: {
+ slots: {
+ memory: "memory-claudemem"
+ },
+ entries: {
+ "memory-claudemem": {
+ config: {
+ workerPath: "/path/to/claude-mem/plugin/scripts/worker-service.cjs"
+ }
+ }
+ }
+ }
+}
+
## Enabling the Moltbot Plugin
Once claude-mem is installed, enable the Moltbot plugin:
@@ -147,6 +166,7 @@ Or explicitly in entries:
|--------|------|---------|-------------|
| `syncMemoryFile` | boolean | `true` | Sync MEMORY.md with claude-mem context on session/gateway start |
| `project` | string | workspace dir name | Project name for scoping observations |
+| `workerPath` | string | (auto-detect) | Path to `worker-service.cjs` for manual installs |
## How It Works
diff --git a/extensions/memory-claudemem/index.ts b/extensions/memory-claudemem/index.ts
index 3cb4e74ba..855367f5d 100644
--- a/extensions/memory-claudemem/index.ts
+++ b/extensions/memory-claudemem/index.ts
@@ -78,6 +78,7 @@ async function getHookCwd(workspaceDir: string, project: string): Promise = {
@@ -170,36 +171,50 @@ function callHookFireAndForget(hookName: string, data: Record):
}
export default function (api: ClawdbotPluginApi) {
- // Find the latest claude-mem worker service (sync check)
- const cacheDir = join(homedir(), ".claude/plugins/cache/thedotmack/claude-mem");
- if (!existsSync(cacheDir)) {
- api.logger.warn?.("claude-mem: plugin cache not found - plugin disabled");
- return;
- }
+ const userConfig = api.pluginConfig as Partial;
- // Find latest version synchronously
- try {
- const entries = require("fs").readdirSync(cacheDir);
- let latestVersion: string | null = null;
- let latestMtime = 0;
+ // Check for custom worker path first (for manual installs)
+ if (userConfig.workerPath) {
+ if (existsSync(userConfig.workerPath)) {
+ WORKER_SERVICE_PATH = userConfig.workerPath;
+ api.logger.debug?.(`claude-mem: using custom worker path: ${WORKER_SERVICE_PATH}`);
+ } else {
+ api.logger.warn?.(`claude-mem: custom workerPath not found: ${userConfig.workerPath} - plugin disabled`);
+ return;
+ }
+ } else {
+ // Auto-discover from Claude plugin cache
+ const cacheDir = join(homedir(), ".claude/plugins/cache/thedotmack/claude-mem");
+ if (!existsSync(cacheDir)) {
+ api.logger.warn?.("claude-mem: plugin cache not found and no workerPath configured - plugin disabled");
+ api.logger.warn?.("claude-mem: install via Claude Code or set plugins.entries.memory-claudemem.config.workerPath");
+ return;
+ }
- for (const entry of entries) {
- const fullPath = join(cacheDir, entry);
- const workerPath = join(fullPath, "scripts/worker-service.cjs");
- if (existsSync(workerPath)) {
- const stats = require("fs").statSync(fullPath);
- if (stats.mtimeMs > latestMtime) {
- latestMtime = stats.mtimeMs;
- latestVersion = entry;
+ // Find latest version synchronously
+ try {
+ const entries = require("fs").readdirSync(cacheDir);
+ let latestVersion: string | null = null;
+ let latestMtime = 0;
+
+ for (const entry of entries) {
+ const fullPath = join(cacheDir, entry);
+ const workerPath = join(fullPath, "scripts/worker-service.cjs");
+ if (existsSync(workerPath)) {
+ const stats = require("fs").statSync(fullPath);
+ if (stats.mtimeMs > latestMtime) {
+ latestMtime = stats.mtimeMs;
+ latestVersion = entry;
+ }
}
}
+
+ if (latestVersion) {
+ WORKER_SERVICE_PATH = join(cacheDir, latestVersion, "scripts/worker-service.cjs");
+ }
+ } catch (err) {
+ api.logger.warn?.("claude-mem: failed to find worker service", err);
}
-
- if (latestVersion) {
- WORKER_SERVICE_PATH = join(cacheDir, latestVersion, "scripts/worker-service.cjs");
- }
- } catch (err) {
- api.logger.warn?.("claude-mem: failed to find worker service", err);
}
if (!WORKER_SERVICE_PATH) {
@@ -212,7 +227,6 @@ export default function (api: ClawdbotPluginApi) {
const workspaceDir = api.workspaceDir || process.cwd();
const defaultProject = basename(workspaceDir);
- const userConfig = api.pluginConfig as Partial;
const config: ClaudeMemConfig = {
...DEFAULT_CONFIG,
project: defaultProject,
diff --git a/extensions/memory-claudemem/moltbot.plugin.json b/extensions/memory-claudemem/moltbot.plugin.json
index 916741cab..96e5089a4 100644
--- a/extensions/memory-claudemem/moltbot.plugin.json
+++ b/extensions/memory-claudemem/moltbot.plugin.json
@@ -16,6 +16,10 @@
"type": "boolean",
"default": true,
"description": "Sync MEMORY.md with claude-mem context on session start"
+ },
+ "workerPath": {
+ "type": "string",
+ "description": "Custom path to worker-service.cjs (for manual installs outside Claude plugin cache)"
}
}
},
@@ -27,6 +31,10 @@
"syncMemoryFile": {
"label": "Sync MEMORY.md",
"help": "Update MEMORY.md with claude-mem context on session/gateway start"
+ },
+ "workerPath": {
+ "label": "Worker Path",
+ "help": "Path to claude-mem's worker-service.cjs. Only needed for manual installs."
}
}
}
diff --git a/package.json b/package.json
index f6d67ab67..562429e48 100644
--- a/package.json
+++ b/package.json
@@ -194,6 +194,7 @@
"long": "5.3.2",
"markdown-it": "^14.1.0",
"node-edge-tts": "^1.2.9",
+ "node-gyp": "^12.2.0",
"osc-progress": "^0.3.0",
"pdfjs-dist": "^5.4.530",
"playwright-core": "1.58.0",
@@ -286,7 +287,9 @@
},
"trustedDependencies": [
"@whiskeysockets/baileys",
+ "esbuild",
"node-llama-cpp",
- "protobufjs"
+ "protobufjs",
+ "sharp"
]
}