diff --git a/electron/main/index.ts b/electron/main/index.ts index 84575d35a..83da79b4b 100644 --- a/electron/main/index.ts +++ b/electron/main/index.ts @@ -17,6 +17,7 @@ import { ClawHubService } from '../gateway/clawhub'; import { ensureClawXContext, repairClawXOnlyBootstrapFiles } from '../utils/openclaw-workspace'; import { autoInstallCliIfNeeded, generateCompletionCache, installCompletionToProfile } from '../utils/openclaw-cli'; import { isQuitting, setQuitting } from './app-state'; +import { ensureBuiltinSkillsInstalled } from '../utils/skill-config'; // Disable GPU hardware acceleration globally for maximum stability across // all GPU configurations (no GPU, integrated, discrete). @@ -187,6 +188,12 @@ async function initialize(): Promise { logger.warn('Failed to repair bootstrap files:', error); }); + // Pre-deploy built-in skills (feishu-doc, feishu-drive, feishu-perm, feishu-wiki) + // to ~/.openclaw/skills/ so they are immediately available without manual install. + void ensureBuiltinSkillsInstalled().catch((error) => { + logger.warn('Failed to install built-in skills:', error); + }); + // Start Gateway automatically (this seeds missing bootstrap files with full templates) try { logger.debug('Auto-starting Gateway...'); diff --git a/electron/utils/skill-config.ts b/electron/utils/skill-config.ts index 2097bc996..015f4f4f8 100644 --- a/electron/utils/skill-config.ts +++ b/electron/utils/skill-config.ts @@ -5,10 +5,13 @@ * * All file I/O uses async fs/promises to avoid blocking the main thread. */ -import { readFile, writeFile, access } from 'fs/promises'; +import { readFile, writeFile, access, cp, mkdir } from 'fs/promises'; +import { existsSync } from 'fs'; import { constants } from 'fs'; import { join } from 'path'; import { homedir } from 'os'; +import { getOpenClawDir } from './paths'; +import { logger } from './logger'; const OPENCLAW_CONFIG_PATH = join(homedir(), '.openclaw', 'openclaw.json'); @@ -132,3 +135,50 @@ export async function getAllSkillConfigs(): Promise> const config = await readConfig(); return config.skills?.entries || {}; } + +/** + * Built-in skills bundled with ClawX that should be pre-deployed to + * ~/.openclaw/skills/ on first launch. These come from the openclaw package's + * extensions directory and are available in both dev and packaged builds. + */ +const BUILTIN_SKILLS = [ + { slug: 'feishu-doc', sourceExtension: 'feishu' }, + { slug: 'feishu-drive', sourceExtension: 'feishu' }, + { slug: 'feishu-perm', sourceExtension: 'feishu' }, + { slug: 'feishu-wiki', sourceExtension: 'feishu' }, +] as const; + +/** + * Ensure built-in skills are deployed to ~/.openclaw/skills//. + * Skips any skill that already has a SKILL.md present (idempotent). + * Runs at app startup; all errors are logged and swallowed so they never + * block the normal startup flow. + */ +export async function ensureBuiltinSkillsInstalled(): Promise { + const skillsRoot = join(homedir(), '.openclaw', 'skills'); + + for (const { slug, sourceExtension } of BUILTIN_SKILLS) { + const targetDir = join(skillsRoot, slug); + const targetManifest = join(targetDir, 'SKILL.md'); + + if (existsSync(targetManifest)) { + continue; // already installed + } + + const openclawDir = getOpenClawDir(); + const sourceDir = join(openclawDir, 'extensions', sourceExtension, 'skills', slug); + + if (!existsSync(join(sourceDir, 'SKILL.md'))) { + logger.warn(`Built-in skill source not found, skipping: ${sourceDir}`); + continue; + } + + try { + await mkdir(targetDir, { recursive: true }); + await cp(sourceDir, targetDir, { recursive: true }); + logger.info(`Installed built-in skill: ${slug} -> ${targetDir}`); + } catch (error) { + logger.warn(`Failed to install built-in skill ${slug}:`, error); + } + } +}