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 <noreply@anthropic.com>
This commit is contained in:
Alex Newman 2026-01-28 14:04:28 -05:00
parent b4919224df
commit 346cef2488
6 changed files with 171 additions and 0 deletions

View File

@ -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 }
}
}
}

View File

@ -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<boolean> {
// TODO: Phase 3
return false;
}
async observe(
sessionId: string,
toolName: string,
toolInput: unknown,
toolResponse: unknown,
): Promise<void> {
// TODO: Phase 3
}
async search(query: string, limit = 10): Promise<SearchResult[]> {
// TODO: Phase 3
return [];
}
async getObservations(ids: number[]): Promise<Observation[]> {
// TODO: Phase 3
return [];
}
}

View File

@ -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<string, unknown>,
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<string, unknown>;
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",
},
},
};

View File

@ -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;

View File

@ -0,0 +1,13 @@
{
"name": "@clawdbot/memory-claudemem",
"version": "0.0.1",
"type": "module",
"main": "index.ts",
"dependencies": {},
"devDependencies": {
"clawdbot": "workspace:*"
},
"peerDependencies": {
"clawdbot": "*"
}
}

View File

@ -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;
};