From 3665428066346f39e0ddc5dfc271ffa96c6735ad Mon Sep 17 00:00:00 2001 From: yezzero Date: Tue, 3 Mar 2026 05:55:53 +0000 Subject: [PATCH] feat: add openclaw tool target for skill sync --- README.ko.md | 1 + README.md | 1 + package.json | 3 +- src/tools/constants.js | 10 +++++- src/tools/openclaw/index.js | 72 +++++++++++++++++++++++++++++++++++++ 5 files changed, 85 insertions(+), 2 deletions(-) create mode 100644 src/tools/openclaw/index.js diff --git a/README.ko.md b/README.ko.md index f9c1a5a..98ba782 100644 --- a/README.ko.md +++ b/README.ko.md @@ -37,6 +37,7 @@ _프롬프트 문맥에 맞는 Skill을 자동 로드하는 모습입니다._ | Copilot | `copilot` | `.github/instructions/*.instructions.md` | | Codex | `codex` | `.agents/skills/*/SKILL.md` | | Antigravity | `antigravity` | `.agent/skills/*/SKILL.md` | +| OpenClaw | `openclaw` | `~/.openclaw/skills/*/SKILL.md` | ## How to Use diff --git a/README.md b/README.md index 8a2df34..0b55682 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,7 @@ _This shows Skills being automatically loaded based on prompt context._ | Copilot | `copilot` | `.github/instructions/*.instructions.md` | | Codex | `codex` | `.agents/skills/*/SKILL.md` | | Antigravity | `antigravity` | `.agent/skills/*/SKILL.md` | +| OpenClaw | `openclaw` | `~/.openclaw/skills/*/SKILL.md` | ## How to Use diff --git a/package.json b/package.json index 47a6777..fc648e6 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,8 @@ "claude-code", "copilot", "codex", - "antigravity" + "antigravity", + "openclaw" ], "homepage": "https://github.com/MosslandOpenDevs/heymark#readme", "bugs": { diff --git a/src/tools/constants.js b/src/tools/constants.js index b781d7f..f01694d 100644 --- a/src/tools/constants.js +++ b/src/tools/constants.js @@ -41,4 +41,12 @@ const CURSOR = { OUTPUT_PATTERN: ".cursor/rules/*.mdc", }; -module.exports = { ANTIGRAVITY, CLAUDE_CODE, CODEX, COPILOT, CURSOR }; +const OPENCLAW = { + KEY: "openclaw", + NAME: "OpenClaw", + SKILLS_DIR: path.join(".openclaw", "skills"), + SKILL_FILE_NAME: "SKILL.md", + OUTPUT_PATTERN: "~/.openclaw/skills/*/SKILL.md", +}; + +module.exports = { ANTIGRAVITY, CLAUDE_CODE, CODEX, COPILOT, CURSOR, OPENCLAW }; diff --git a/src/tools/openclaw/index.js b/src/tools/openclaw/index.js new file mode 100644 index 0000000..0ad59fb --- /dev/null +++ b/src/tools/openclaw/index.js @@ -0,0 +1,72 @@ +const fs = require("fs"); +const path = require("path"); +const os = require("os"); +const { OPENCLAW } = require("@/tools/constants"); + +function createContent(skill) { + const frontmatterLines = [ + "---", + `name: ${skill.name}`, + `description: "${skill.description}"`, + "---", + ]; + + return `${frontmatterLines.join("\n")}\n\n${skill.body}\n`; +} + +function getSkillsDir() { + return path.join(os.homedir(), OPENCLAW.SKILLS_DIR); +} + +function generate(skills) { + const skillsDir = getSkillsDir(); + + for (const skill of skills) { + try { + const skillDir = path.join(skillsDir, skill.name); + fs.mkdirSync(skillDir, { recursive: true }); + fs.writeFileSync(path.join(skillDir, OPENCLAW.SKILL_FILE_NAME), createContent(skill), "utf8"); + } catch (err) { + const skillDir = path.join(skillsDir, skill.name); + throw new Error(`OpenClaw: failed to generate skill "${skill.name}" at ${skillDir}: ${err.message}`); + } + } + + return skills.length; +} + +function clean(skillNames) { + const skillsDir = getSkillsDir(); + if (!fs.existsSync(skillsDir)) { + return []; + } + + const cleanedPaths = []; + for (const skillName of skillNames) { + const skillDir = path.join(skillsDir, skillName); + if (!fs.existsSync(skillDir)) continue; + + try { + fs.rmSync(skillDir, { recursive: true, force: true }); + cleanedPaths.push(path.join(skillsDir, skillName)); + } catch (err) { + throw new Error(`OpenClaw: failed to clean skill "${skillName}" at ${skillDir}: ${err.message}`); + } + } + + return cleanedPaths; +} + +module.exports = { + key: OPENCLAW.KEY, + name: OPENCLAW.NAME, + output: OPENCLAW.OUTPUT_PATTERN, + + generate(skills) { + return generate(skills); + }, + + clean(skillNames) { + return clean(skillNames); + }, +};