diff --git a/packages/agents/src/clawdbot.ts b/packages/agents/src/clawdbot.ts index 2f56a339..6dab33b2 100644 --- a/packages/agents/src/clawdbot.ts +++ b/packages/agents/src/clawdbot.ts @@ -69,10 +69,9 @@ ${skillsXml} } async isDetected(): Promise { - const projectSkills = join(process.cwd(), 'skills'); const globalClawdbot = join(homedir(), '.clawdbot'); const clawdbotConfig = join(process.cwd(), 'clawdbot.json'); - return existsSync(projectSkills) || existsSync(globalClawdbot) || existsSync(clawdbotConfig); + return existsSync(globalClawdbot) || existsSync(clawdbotConfig); } } diff --git a/packages/agents/src/index.ts b/packages/agents/src/index.ts index c36169b6..8b34787e 100644 --- a/packages/agents/src/index.ts +++ b/packages/agents/src/index.ts @@ -8,6 +8,7 @@ import { OpenCodeAdapter } from './opencode.js'; import { AntigravityAdapter } from './antigravity.js'; import { AmpAdapter } from './amp.js'; import { ClawdbotAdapter } from './clawdbot.js'; +import { OpenClawAdapter } from './openclaw.js'; import { DroidAdapter } from './droid.js'; import { FactoryAdapter } from './factory.js'; import { GitHubCopilotAdapter } from './github-copilot.js'; @@ -29,6 +30,7 @@ export * from './opencode.js'; export * from './antigravity.js'; export * from './amp.js'; export * from './clawdbot.js'; +export * from './openclaw.js'; export * from './droid.js'; export * from './factory.js'; export * from './github-copilot.js'; @@ -53,6 +55,7 @@ const adapters: Record = { antigravity: new AntigravityAdapter(), amp: new AmpAdapter(), clawdbot: new ClawdbotAdapter(), + openclaw: new OpenClawAdapter(), droid: new DroidAdapter(), 'github-copilot': new GitHubCopilotAdapter(), goose: new GooseAdapter(), @@ -116,6 +119,7 @@ export async function detectAgent(): Promise { 'opencode', 'antigravity', 'amp', + 'openclaw', 'clawdbot', 'droid', 'github-copilot', diff --git a/packages/agents/src/openclaw.ts b/packages/agents/src/openclaw.ts new file mode 100644 index 00000000..5e6f28be --- /dev/null +++ b/packages/agents/src/openclaw.ts @@ -0,0 +1,99 @@ +import { existsSync } from 'node:fs'; +import { join } from 'node:path'; +import { homedir } from 'node:os'; +import type { AgentAdapter } from './base.js'; +import { createSkillXml } from './base.js'; +import type { Skill, AgentType } from '@skillkit/core'; +import { AGENT_CONFIG } from '@skillkit/core'; + +const config = AGENT_CONFIG.openclaw; + +/** + * OpenClaw Agent Adapter + * + * OpenClaw (formerly Clawdbot) is a local-first AI agent framework with a + * persistent gateway daemon. Skills use an extended YAML frontmatter schema + * with `permissions`, `triggers`, and `metadata.openclaw.requires` fields. + * + * Key differences from Claude Code: + * - Skills dir: `skills/` (not `.claude/skills/`) + * - Global skills: `~/.openclaw/skills/` + * - Config: `~/.openclaw/openclaw.json` + * - SKILL.md frontmatter includes: permissions, triggers, metadata.openclaw + * - Gateway loads skills contextually per-agent at runtime + * - Skills are organized by department: skills///SKILL.md + */ +export class OpenClawAdapter implements AgentAdapter { + readonly type: AgentType = 'openclaw'; + readonly name = 'OpenClaw'; + readonly skillsDir = config.skillsDir; + readonly configFile = config.configFile; + + generateConfig(skills: Skill[]): string { + const enabledSkills = skills.filter(s => s.enabled); + + if (enabledSkills.length === 0) { + return ''; + } + + const skillsXml = enabledSkills.map(createSkillXml).join('\n\n'); + + return ` + +## Available Skills + + + +When users ask you to perform tasks, check if any of the available skills below can help complete the task more effectively. Skills provide specialized capabilities and domain knowledge. + +How to use skills: +- Invoke: \`skillkit read \` or \`npx skillkit read \` +- The skill content will load with detailed instructions on how to complete the task +- Base directory provided in output for resolving bundled resources (references/, scripts/, assets/) + +Usage notes: +- Only use skills listed in below +- Do not invoke a skill that is already loaded in your context +- Each skill invocation is stateless + + + + +${skillsXml} + + + + +`; + } + + parseConfig(content: string): string[] { + const skillNames: string[] = []; + const skillRegex = /([^<]+)<\/name>/g; + let match; + + while ((match = skillRegex.exec(content)) !== null) { + skillNames.push(match[1].trim()); + } + + return skillNames; + } + + getInvokeCommand(skillName: string): string { + return `skillkit read ${skillName}`; + } + + async isDetected(): Promise { + // OpenClaw workspace: skills/ dir at project root + const projectSkills = join(process.cwd(), 'skills'); + // OpenClaw global config + const globalOpenClaw = join(homedir(), '.openclaw'); + // OpenClaw config file + const openclawConfig = join(process.cwd(), 'openclaw.json'); + + return ( + (existsSync(projectSkills) && existsSync(globalOpenClaw)) || + existsSync(openclawConfig) + ); + } +} diff --git a/packages/core/src/agent-config.ts b/packages/core/src/agent-config.ts index cfe4f772..e271bcc0 100644 --- a/packages/core/src/agent-config.ts +++ b/packages/core/src/agent-config.ts @@ -111,13 +111,33 @@ export const AGENT_CONFIG: Record = { supportsAutoDiscovery: true, }, - // Clawdbot + // Clawdbot (legacy name for OpenClaw) clawdbot: { - skillsDir: '.clawdbot/skills', - configFile: 'AGENTS.md', - altSkillsDirs: ['skills'], + skillsDir: 'skills', + configFile: 'CLAUDE.md', + altSkillsDirs: ['.clawdbot/skills', '~/.openclaw/skills'], + globalSkillsDir: '~/.openclaw/skills', + configFormat: 'xml', + usesFrontmatter: true, + frontmatterFields: [ + 'name', 'description', 'version', 'scan_exempt', + 'permissions', 'triggers', 'metadata', + ], + supportsAutoDiscovery: true, + }, + + // OpenClaw (same as clawdbot — rebranded) + openclaw: { + skillsDir: 'skills', + configFile: 'CLAUDE.md', + altSkillsDirs: ['~/.openclaw/skills'], + globalSkillsDir: '~/.openclaw/skills', configFormat: 'xml', usesFrontmatter: true, + frontmatterFields: [ + 'name', 'description', 'version', 'scan_exempt', + 'permissions', 'triggers', 'metadata', + ], supportsAutoDiscovery: true, }, diff --git a/packages/core/src/agents/types.ts b/packages/core/src/agents/types.ts index beaacb92..f7ea4a65 100644 --- a/packages/core/src/agents/types.ts +++ b/packages/core/src/agents/types.ts @@ -227,6 +227,7 @@ export const AGENT_DISCOVERY_PATHS: Record = { 'antigravity': ['.antigravity/agents'], 'amp': ['.amp/agents'], 'clawdbot': ['.clawdbot/agents', 'agents'], + 'openclaw': ['agents', '.openclaw/agents'], 'droid': ['.droid/agents'], 'github-copilot': ['.github/agents', '.github/instructions', '.github/custom-agents'], 'goose': ['.goose/agents'], @@ -277,6 +278,7 @@ export const ALL_AGENT_DISCOVERY_PATHS = [ '.cline/agents', '.clawdbot/agents', '.codebuddy/agents', + '.openclaw/agents', '.codex/agents', '.commandcode/agents', '.continue/agents', @@ -336,6 +338,7 @@ export const CUSTOM_AGENT_FORMAT_MAP: Record = { 'antigravity': 'claude-agent', 'amp': 'claude-agent', 'clawdbot': 'claude-agent', + 'openclaw': 'claude-agent', 'droid': 'claude-agent', 'github-copilot': 'universal', 'goose': 'claude-agent', diff --git a/packages/core/src/commands/generator.ts b/packages/core/src/commands/generator.ts index 932d314d..f2f01e21 100644 --- a/packages/core/src/commands/generator.ts +++ b/packages/core/src/commands/generator.ts @@ -73,6 +73,13 @@ const AGENT_FORMATS: Record = { supportsSlashCommands: true, supportsCommandFiles: true, }, + openclaw: { + agent: 'openclaw', + extension: '.md', + directory: '.claude/commands', + supportsSlashCommands: true, + supportsCommandFiles: true, + }, droid: { agent: 'droid', extension: '.md', diff --git a/packages/core/src/translator/types.ts b/packages/core/src/translator/types.ts index 1d6bd7b1..02c4418a 100644 --- a/packages/core/src/translator/types.ts +++ b/packages/core/src/translator/types.ts @@ -21,6 +21,7 @@ export const AGENT_FORMAT_MAP: Record = { 'antigravity': 'skill-md', 'amp': 'skill-md', 'clawdbot': 'skill-md', + 'openclaw': 'skill-md', 'droid': 'skill-md', 'goose': 'skill-md', 'kilo': 'skill-md', diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 7ae424d4..d5c04586 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -9,6 +9,7 @@ export const AgentType = z.enum([ "gemini-cli", "amp", "clawdbot", + "openclaw", "droid", "github-copilot", "goose",