如果說 ClawHub 是市場,那 skill 比較像市場裡的「可帶走的工作說明書」。
它不是 runtime 本體,也不是整個平台的骨架。
它比較像一個 agent 碰到某個情境時,可以拿來照著做的專用手冊、步驟集、或作戰方案。
第 27 天我想把這件事講清楚:
skill 在 ClawHub 裡到底扮演什麼角色?它只是可安裝內容,還是會真的影響 OpenClaw 怎麼工作?
答案是後者。
skill 不只是資料,它會進到 discovery、readiness、install、doctor 這幾條路徑,最後變成 agent 能不能用、該不該用、怎麼用的判準。
我會用三個角度看 skill:
這三步看起來簡單,但其實每一步都在做不同事情。
這也是 skill 比 plugin 更像「工作單元」的原因。
skill 的價值不是存在,而是可被 agent 消化。
先看 skill status 的核心資料結構。
📄 原始碼:
src/skills/discovery/status.ts:46-77
export type SkillStatusEntry = {
name: string;
description: string;
source: string;
bundled: boolean;
filePath: string;
baseDir: string;
skillKey: string;
primaryEnv?: string;
emoji?: string;
homepage?: string;
always: boolean;
disabled: boolean;
blockedByAllowlist: boolean;
blockedByAgentFilter: boolean;
eligible: boolean;
platformIncompatible: boolean;
modelVisible: boolean;
userInvocable: boolean;
commandVisible: boolean;
requirements: Requirements;
missing: Requirements;
configChecks: RequirementConfigCheck[];
install: SkillInstallOption[];
clawhub?: ClawHubSkillStatusLink;
skillCard?: LocalSkillCardStatus;
};
這一長串欄位其實很誠實。
OpenClaw 並沒有把 skill 簡化成「有」或「沒有」。
它把 skill 當成一個有條件、有來源、有狀態、有介面的實體。
再看 status 是怎麼做出來的。
📄 原始碼:
src/skills/discovery/status.ts:290-296
const eligible = !disabled && !blockedByAllowlist && requirementsSatisfied;
const platformIncompatible = missing.os.length > 0;
const availableToAgent = eligible && !blockedByAgentFilter;
這三行很關鍵。
disabled 是配置層面關掉blockedByAllowlist 是策略層面不讓進requirementsSatisfied 是環境層面能不能跑blockedByAgentFilter 則是「這個 agent 能不能碰」也就是說,skill 的可用性不是一刀切,而是多層過濾後的結果。
再看它怎麼決定 install 選項。
📄 原始碼:
src/skills/discovery/status.ts:139-156
function selectPreferredInstallSpec(
install: SkillInstallSpec[],
prefs: SkillsInstallPreferences,
): { spec: SkillInstallSpec; index: number } | undefined {
if (install.length === 0) {
return undefined;
}
const brewSpec = findKind("brew");
const nodeSpec = findKind("node");
const goSpec = findKind("go");
const uvSpec = findKind("uv");
const downloadSpec = findKind("download");
const brewAvailable = hasBinary("brew");
這裡的意思是:
這個設計很像人類的現場操作。
你可以同時提供:
但面對使用者時,通常只先講一條最合理的路徑,不會把全部選項一次倒出來。
再看 onboarding 怎麼用這些 status。
📄 原始碼:
src/commands/onboard-skills.ts:150-156
const report = buildWorkspaceSkillStatus(workspaceDir, { config: cfg });
const eligible = report.skills.filter((s) => s.eligible);
const unsupportedOs = report.skills.filter(
(s) => !s.disabled && !s.blockedByAllowlist && s.missing.os.length > 0,
);
const missing = report.skills.filter(
(s) => !s.eligible && !s.disabled && !s.blockedByAllowlist && s.missing.os.length === 0,
);
這段就是 skill 舞台化之後的用法。
OpenClaw 不是只知道 skill 存在,而是會把它們分成:
這很像一個管理員在看名冊,不是只看名字,而是看現況。
再看 doctor 怎麼處理。
📄 原始碼:
src/commands/doctor-skills.ts:90-97
const unavailable = collectUnavailableAgentSkills(report);
if (unavailable.length === 0) {
return params.cfg;
}
note(formatUnavailableSkillDoctorLines(unavailable).join("\n"), "Skills");
const shouldDisable = await params.prompter.confirmAutoFix({
message: `Disable ${unavailable.length} unavailable skill${unavailable.length === 1 ? "" : "s"} in config?`,
initialValue: false,
});
doctor 的角色很明確:
這代表 skill 不只是安裝時的故事,而是持續維護的故事。
最後再看 skill 跟 ClawHub 的連結。
📄 原始碼:
src/skills/discovery/status.ts:303-307
const clawhub =
workspaceDir && !bundled
? resolveClawHubSkillStatusLinkSync({
workspaceDir,
skillDir: entry.skill.baseDir,
skillKey,
lockRead: context.clawhubLockRead,
lockfileScope: "workspace",
})
: undefined;
這個 link 很有意思。
它把 skill 的現況連回 ClawHub 的 origin / lock 記錄,讓 skill 不會只是「目錄裡的一個資料夾」,而是「有來源的 install 實體」。
skill 在 ClawHub 裡,像是一本可更新的作戰手冊。
你不是把它裝進來就結束,而是要確認:
這也是 skill 跟 plugin 最大的不同之一。
系統擴充要處理的是 runtime、config、provider、route。
工作能力要處理的是任務是否能執行、使用者是否能理解、agent 是否知道該不該用。
所以 skill 在 ClawHub 裡扮演的不是被動檔案,而是「會被判斷、會被推薦、會被修復」的活物。
但這個設計很符合真實世界。
因為 skill 不是展示品,它會活在長期系統裡。