From 2c05c43dc8b66ae37501e42a9747c07d82002185 Mon Sep 17 00:00:00 2001 From: Martin Kessler Date: Wed, 8 Apr 2026 14:13:43 -0700 Subject: [PATCH] fix(openclaw): use sync plugin registration (#498) Co-authored-by: Niemand Assistant --- src/converters/claude-to-openclaw.ts | 41 +++------------------------- tests/openclaw-converter.test.ts | 6 ++-- 2 files changed, 8 insertions(+), 39 deletions(-) diff --git a/src/converters/claude-to-openclaw.ts b/src/converters/claude-to-openclaw.ts index a74ddf3..50113dc 100644 --- a/src/converters/claude-to-openclaw.ts +++ b/src/converters/claude-to-openclaw.ts @@ -177,17 +177,16 @@ function buildOpenClawConfig( function generateEntryPoint(commands: OpenClawCommandRegistration[]): string { const commandRegistrations = commands .map((cmd) => { - // JSON.stringify produces a fully-escaped string literal safe for JS/TS source embedding const safeName = JSON.stringify(cmd.name) const safeDesc = JSON.stringify(cmd.description ?? "") - const safeNotFound = JSON.stringify(`Command ${cmd.name} not found. Check skills directory.`) + const safeBody = JSON.stringify(cmd.body) return ` api.registerCommand({ name: ${safeName}, description: ${safeDesc}, acceptsArgs: ${cmd.acceptsArgs}, requireAuth: false, - handler: (ctx) => ({ - text: skills[${safeName}] ?? ${safeNotFound}, + handler: () => ({ + text: ${safeBody}, }), });` }) @@ -195,39 +194,7 @@ function generateEntryPoint(commands: OpenClawCommandRegistration[]): string { return `// Auto-generated OpenClaw plugin entry point // Converted from Claude Code plugin format by compound-plugin CLI -import { promises as fs } from "fs"; -import path from "path"; -import { fileURLToPath } from "url"; - -const __dirname = path.dirname(fileURLToPath(import.meta.url)); - -// Pre-load skill bodies for command responses -const skills: Record = {}; - -async function loadSkills() { - const skillsDir = path.join(__dirname, "skills"); - try { - const entries = await fs.readdir(skillsDir, { withFileTypes: true }); - for (const entry of entries) { - if (!entry.isDirectory()) continue; - const skillPath = path.join(skillsDir, entry.name, "SKILL.md"); - try { - const content = await fs.readFile(skillPath, "utf8"); - // Strip frontmatter - const body = content.replace(/^---[\\s\\S]*?---\\n*/, ""); - skills[entry.name.replace(/^cmd-/, "")] = body.trim(); - } catch { - // Skill file not found, skip - } - } - } catch { - // Skills directory not found - } -} - -export default async function register(api) { - await loadSkills(); - +export default function register(api) { ${commandRegistrations} } ` diff --git a/tests/openclaw-converter.test.ts b/tests/openclaw-converter.test.ts index 813c4bc..ab507db 100644 --- a/tests/openclaw-converter.test.ts +++ b/tests/openclaw-converter.test.ts @@ -231,9 +231,11 @@ describe("convertClaudeToOpenClaw", () => { expect(nameLine).toBeDefined() }) - test("generateEntryPoint emits typed skills record", () => { + test("generateEntryPoint inlines command bodies for sync registration", () => { const bundle = convertClaudeToOpenClaw(fixturePlugin, defaultOptions) - expect(bundle.entryPoint).toContain("const skills: Record = {}") + expect(bundle.entryPoint).not.toContain("const skills: Record = {}") + expect(bundle.entryPoint).toContain('text: "Plan the work. See ~/.openclaw/settings for config."') + expect(bundle.entryPoint).toContain("export default function register(api)") }) test("plugin without MCP servers has no openclawConfig", () => {