惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

D
Docker
人人都是产品经理
人人都是产品经理
小众软件
小众软件
博客园 - Franky
WordPress大学
WordPress大学
Jina AI
Jina AI
Google DeepMind News
Google DeepMind News
I
InfoQ
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
F
Fortinet All Blogs
博客园 - 【当耐特】
IT之家
IT之家
G
Google Developers Blog
J
Java Code Geeks
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
云风的 BLOG
云风的 BLOG
Recent Announcements
Recent Announcements
有赞技术团队
有赞技术团队
V
Visual Studio Blog
U
Unit 42
阮一峰的网络日志
阮一峰的网络日志
月光博客
月光博客
GbyAI
GbyAI
雷峰网
雷峰网

Recent Commits to openclaw:main

test: merge chat side-result checks · openclaw/openclaw@ddd2c2a test: merge cron history checks · openclaw/openclaw@f7eb746 test: merge responsive navigation shell checks · openclaw/openclaw@c2e4b47 docs(changelog): add codex oauth fixes · openclaw/openclaw@628e6cd test: merge navigation routing cases · openclaw/openclaw@5d8cecb Tests: mock channel registry bundled fallback · openclaw/openclaw@2b08233 Secrets: avoid broad web search discovery for single plugin config · openclaw/openclaw@a464f59 test: merge config view browser checks · openclaw/openclaw@20cf511 fix(status): align oauth health with runtime · openclaw/openclaw@eed7116 feat: add macOS screen snapshots for monitor preview (#67954) thanks … · openclaw/openclaw@f377db1 fix: report shared auth scopes in hello-ok (#67810) thanks @BunsDev · openclaw/openclaw@0b6c39b Auto-reply: avoid eager bundled route fallback · openclaw/openclaw@3ea1bf4 Tests: narrow session binding contract setup · openclaw/openclaw@54e4e16 fix(macOS): enable undo/redo in webchat composer text input (#34962) · openclaw/openclaw@00951dc Tests: speed up channel setup promotion · openclaw/openclaw@82b529a Docs: refresh agent instructions · openclaw/openclaw@5775fe2 fix(auth): serialize OAuth refresh across agents to fix #26322 (#67876) · openclaw/openclaw@8e79080 test: allow ollama public surface boundary test · openclaw/openclaw@7d4f1a6 Docs: add test performance guardrails · openclaw/openclaw@89706d3 Tests: restore context-engine usage proof · openclaw/openclaw@e4c4f95 Tests: slim context engine runtime coverage · openclaw/openclaw@74c198f ci: retry failed custom checkouts · openclaw/openclaw@0ee5baf test: trim duplicate provider auth onboarding cases · openclaw/openclaw@1ffc02e matrix: fix sessions_spawn --thread subagent session spawning (#67643) · openclaw/openclaw@1ce2596 test: reduce auth choice fixture churn · openclaw/openclaw@857b9cd test: mock health status config boundaries · openclaw/openclaw@9d5ab4a test: mock onboard config io boundary · openclaw/openclaw@299694d test: mock legacy state plugin boundaries · openclaw/openclaw@2713089 test: mock channel install boundaries · openclaw/openclaw@b945248 test: mock doctor preview channel boundaries · openclaw/openclaw@b1a3ad4
fix(skills): harden ClawHub update policy · openclaw/open...
vincentkoc · 2026-06-22 · via Recent Commits to openclaw:main
Original file line numberDiff line numberDiff line change

@@ -1,77 +1,64 @@

11

---

22

name: clawhub

3-

description: "Search, install, update, sync, or publish agent skills with the ClawHub CLI and registry."

4-

metadata:

5-

{

6-

"openclaw":

7-

{

8-

"requires": { "bins": ["clawhub"] },

9-

"install":

10-

[

11-

{

12-

"id": "node",

13-

"kind": "node",

14-

"package": "clawhub",

15-

"bins": ["clawhub"],

16-

"label": "Install ClawHub CLI (npm)",

17-

},

18-

],

19-

},

20-

}

3+

description: "Search ClawHub for skills when a requested capability is not already available; install, verify, update, publish, or sync skills."

214

---

225
23-

# ClawHub CLI

6+

# ClawHub

247
25-

Install

8+

Use `openclaw skills` to discover and manage skills for the current OpenClaw

9+

agent. Use the standalone `clawhub` CLI only for publishing, syncing, and

10+

publisher account workflows.

2611
27-

```bash

28-

npm i -g clawhub

29-

```

12+

## Discover skills

3013
31-

Auth (publish)

14+

Search before claiming that a requested capability is unavailable:

3215
3316

```bash

34-

clawhub login

35-

clawhub whoami

17+

openclaw skills search "postgres backups"

3618

```

3719
38-

Search

20+

Before installing, verify the selected skill and treat third-party skills as

21+

untrusted. Obtain user approval before installation.

3922
4023

```bash

41-

clawhub search "postgres backups"

24+

openclaw skills verify my-skill

25+

openclaw skills install my-skill

26+

openclaw skills install my-skill --version 1.2.3

4227

```

4328
44-

Install

29+

## Manage installed skills

4530
4631

```bash

47-

clawhub install my-skill

48-

clawhub install my-skill --version 1.2.3

32+

openclaw skills list

33+

openclaw skills check

34+

openclaw skills update my-skill

35+

openclaw skills update --all

4936

```

5037
51-

Update (hash-based match + upgrade)

38+

Use `--global` with `install` or `update` to manage skills shared by all local

39+

agents.

5240
53-

```bash

54-

clawhub update my-skill

55-

clawhub update my-skill --version 1.2.3

56-

clawhub update --all

57-

clawhub update my-skill --force

58-

clawhub update --all --no-input --force

59-

```

41+

## Publish skills

6042
61-

List

43+

Install the standalone ClawHub CLI for publisher workflows:

6244
6345

```bash

64-

clawhub list

46+

npm i -g clawhub

47+

clawhub login

48+

clawhub whoami

6549

```

6650
67-

Publish

51+

Publish or sync skills:

6852
6953

```bash

70-

clawhub publish ./my-skill --slug my-skill --name "My Skill" --version 1.2.0 --changelog "Fixes + docs"

54+

clawhub skill publish ./my-skill

55+

clawhub skill publish ./my-skill --version 1.2.3

56+

clawhub sync --all

7157

```

7258
73-

Notes

59+

## Notes

7460
75-

- Default registry: https://clawhub.com (override with CLAWHUB_REGISTRY or --registry)

76-

- Default workdir: cwd (falls back to OpenClaw workspace); install dir: ./skills (override with --workdir / --dir / CLAWHUB_WORKDIR)

77-

- Update command hashes local files, resolves matching version, and upgrades to latest unless --version is set

61+

- Public registry: https://clawhub.ai

62+

- `openclaw skills install` installs into the active workspace by default.

63+

- Shared installs use `--global` and are visible to all local agents unless

64+

agent allowlists narrow them.

Original file line numberDiff line numberDiff line change

@@ -680,6 +680,7 @@ describe("skills cli commands", () => {

680680

workspaceDir: "/tmp/workspace",

681681

slug: undefined,

682682

});

683+

expect(updateAllArgs.config).toEqual({});

683684

expectLogger(updateAllArgs.logger);

684685

expect(

685686

runtimeLogs.some((line) => line.includes("Updated calendar: 1.2.2 -> 1.2.3")),

@@ -688,6 +689,24 @@ describe("skills cli commands", () => {

688689

expect(runtimeErrors).toStrictEqual([]);

689690

});

690691
692+

it("does not bootstrap configured skills during update all", async () => {

693+

loadConfigMock.mockReturnValueOnce({

694+

agents: {

695+

defaults: {

696+

skills: ["apple-notes"],

697+

},

698+

},

699+

});

700+

readTrackedClawHubSkillSlugsMock.mockResolvedValue([]);

701+
702+

await runCommand(["skills", "update", "--all"]);

703+
704+

expect(readTrackedClawHubSkillSlugsMock).toHaveBeenCalledWith("/tmp/workspace");

705+

expect(updateSkillsFromClawHubMock).not.toHaveBeenCalled();

706+

expect(runtimeLogs).toContain("No tracked ClawHub skills to update.");

707+

expect(runtimeErrors).toStrictEqual([]);

708+

});

709+
691710

it("passes --force-install through for ClawHub skill updates", async () => {

692711

readTrackedClawHubSkillSlugsMock.mockResolvedValue(["calendar"]);

693712

updateSkillsFromClawHubMock.mockResolvedValue([

@@ -791,6 +810,7 @@ describe("skills cli commands", () => {

791810

workspaceDir: "/tmp/openclaw-config",

792811

slug: undefined,

793812

logger: expect.any(Object),

813+

config: {},

794814

});

795815

});

796816

@@ -817,6 +837,7 @@ describe("skills cli commands", () => {

817837

workspaceDir: "/tmp/openclaw-config",

818838

slug: "calendar",

819839

logger: expect.any(Object),

840+

config: {},

820841

});

821842

});

822843
Original file line numberDiff line numberDiff line change

@@ -149,10 +149,6 @@ async function runSkillsAction(

149149

}

150150

}

151151
152-

function resolveActiveWorkspaceDir(options?: ResolveSkillsWorkspaceOptions): string {

153-

return resolveSkillsWorkspace(options).workspaceDir;

154-

}

155-
156152

function resolveSkillsWorkspaceForCommand(

157153

command: Command | null | undefined,

158154

opts?: { agent?: string },

@@ -164,16 +160,23 @@ function resolveClawHubTargetWorkspaceDir(

164160

command: Command | undefined,

165161

opts: { agent?: string; global?: boolean },

166162

): string | undefined {

163+

return resolveClawHubTargetWorkspace(command, opts)?.workspaceDir;

164+

}

165+
166+

function resolveClawHubTargetWorkspace(

167+

command: Command | undefined,

168+

opts: { agent?: string; global?: boolean },

169+

): Pick<ResolvedSkillsWorkspace, "config" | "workspaceDir"> | undefined {

167170

const agentId = resolveAgentOption(command, opts);

168171

if (opts.global && normalizeOptionalString(agentId)) {

169172

defaultRuntime.error("Use either --global or --agent, not both.");

170173

defaultRuntime.exit(1);

171174

return undefined;

172175

}

173176

if (opts.global) {

174-

return CONFIG_DIR;

177+

return { config: getRuntimeConfig(), workspaceDir: CONFIG_DIR };

175178

}

176-

return resolveActiveWorkspaceDir({ agentId });

179+

return resolveSkillsWorkspace({ agentId });

177180

}

178181
179182

function shouldFailSkillVerification(result: ClawHubSkillVerificationResponse): boolean {

@@ -438,22 +441,23 @@ export function registerSkillsCli(program: Command) {

438441

defaultRuntime.exit(1);

439442

return;

440443

}

441-

const workspaceDir = resolveClawHubTargetWorkspaceDir(command, opts);

442-

if (!workspaceDir) {

444+

const target = resolveClawHubTargetWorkspace(command, opts);

445+

if (!target) {

443446

return;

444447

}

445-

const tracked = await readTrackedClawHubSkillSlugs(workspaceDir);

448+

const tracked = await readTrackedClawHubSkillSlugs(target.workspaceDir);

446449

if (opts.all && tracked.length === 0) {

447450

defaultRuntime.log("No tracked ClawHub skills to update.");

448451

return;

449452

}

450453

const results = await updateSkillsFromClawHub({

451-

workspaceDir,

454+

workspaceDir: target.workspaceDir,

452455

slug,

453456

...(opts.forceInstall ? { forceInstall: true } : {}),

454457

logger: {

455458

info: (message) => defaultRuntime.log(message),

456459

},

460+

config: target.config,

457461

});

458462

let failed = false;

459463

for (const result of results) {

Original file line numberDiff line numberDiff line change

@@ -1104,6 +1104,42 @@ describe("skills-clawhub", () => {

11041104

}

11051105

});

11061106
1107+

it("does not install configured skills during update all without ClawHub tracking", async () => {

1108+

const workspaceDir = await tempDirs.make("openclaw-configured-update-");

1109+

const results = await updateSkillsFromClawHub({

1110+

workspaceDir,

1111+

config: {

1112+

agents: {

1113+

defaults: {

1114+

skills: ["apple-notes", "sherpa-onnx-tts"],

1115+

},

1116+

},

1117+

},

1118+

});

1119+
1120+

expect(results).toEqual([]);

1121+

expect(fetchClawHubSkillInstallResolutionMock).not.toHaveBeenCalled();

1122+

expect(installPackageDirMock).not.toHaveBeenCalled();

1123+

});

1124+
1125+

it("rejects untracked requested updates instead of installing by slug", async () => {

1126+

const workspaceDir = await tempDirs.make("openclaw-untracked-update-");

1127+
1128+

const results = await updateSkillsFromClawHub({

1129+

workspaceDir,

1130+

slug: "calendar",

1131+

});

1132+
1133+

expect(results).toEqual([

1134+

{

1135+

ok: false,

1136+

error: 'Skill "calendar" is not tracked as a ClawHub install.',

1137+

},

1138+

]);

1139+

expect(fetchClawHubSkillInstallResolutionMock).not.toHaveBeenCalled();

1140+

expect(installPackageDirMock).not.toHaveBeenCalled();

1141+

});

1142+
11071143

it("updates a legacy Unicode slug when requested explicitly", async () => {

11081144

const slug = "re\u0430ct";

11091145

const { workspaceDir } = await createLegacyTrackedSkillFixture(slug);