fix: use ClawHub branding for registry

This commit is contained in:
Peter Steinberger
2026-01-31 11:31:46 +01:00
parent 5d6ee7adf3
commit a32498ea7d
28 changed files with 57 additions and 57 deletions
+2 -2
View File
@@ -12,11 +12,11 @@
### Added
- Web: show published skills on user profiles (thanks @njoylab, #20).
- CLI: include OpenClaw + Moltbot fallback skill roots for sync scans.
- CLI: include ClawHub + Moltbot fallback skill roots for sync scans.
- CLI: support OpenClaw configuration files (`OPENCLAW_CONFIG_PATH` / `OPENCLAW_STATE_DIR`).
### Changed
- Brand: rebrand to OpenClaw and publish CLI as `clawhub` (legacy `clawdhub` supported).
- Brand: rebrand to ClawHub and publish CLI as `clawhub` (legacy `clawdhub` supported).
- Domain: default site/registry now `https://clawhub.ai`; `.well-known/clawhub.json` preferred.
- Theme: persist theme under `clawhub-theme` (legacy key still read).
+5 -5
View File
@@ -1,4 +1,4 @@
# OpenClaw
# ClawHub
<p align="center">
<a href="https://github.com/openclaw/clawhub/actions/workflows/ci.yml?branch=main"><img src="https://img.shields.io/github/actions/workflow/status/openclaw/clawhub/ci.yml?branch=main&style=for-the-badge" alt="CI status"></a>
@@ -6,7 +6,7 @@
<a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-blue.svg?style=for-the-badge" alt="MIT License"></a>
</p>
OpenClaw is the **public skill registry for Clawdbot**: publish, version, and search text-based agent skills (a `SKILL.md` plus supporting files).
ClawHub is the **public skill registry for Clawdbot**: publish, version, and search text-based agent skills (a `SKILL.md` plus supporting files).
Its designed for fast browsing + a CLI-friendly API, with moderation hooks and vector search.
onlycrabs.ai is the **SOUL.md registry**: publish and share system lore the same way you publish skills.
@@ -27,7 +27,7 @@ onlycrabs.ai: `https://onlycrabs.ai`
- Entry point is host-based: `onlycrabs.ai`.
- On the onlycrabs.ai host, the home page and nav default to souls.
- On OpenClaw, souls live under `/souls`.
- On ClawHub, souls live under `/souls`.
- Soul bundles only accept `SOUL.md` for now (no extra files).
## How it works (high level)
@@ -40,7 +40,7 @@ onlycrabs.ai: `https://onlycrabs.ai`
## Telemetry
OpenClaw tracks minimal **install telemetry** (to compute install counts) when you run `clawhub sync` while logged in.
ClawHub tracks minimal **install telemetry** (to compute install counts) when you run `clawhub sync` while logged in.
Disable via:
```bash
@@ -96,7 +96,7 @@ This writes `JWT_PRIVATE_KEY` + `JWKS` to the deployment and prints values for y
## Nix plugins (nixmode skills)
OpenClaw can store a nix-clawdbot plugin pointer in SKILL frontmatter so the registry knows which
ClawHub can store a nix-clawdbot plugin pointer in SKILL frontmatter so the registry knows which
Nix package bundle to install. A nix plugin is different from a regular skill pack: it bundles the
skill pack, the CLI binary, and its config flags/requirements together.
+4 -4
View File
@@ -72,7 +72,7 @@ export function buildDiscordPayload(
},
],
footer: {
text: 'OpenClaw',
text: 'ClawHub',
},
timestamp: new Date().toISOString(),
},
@@ -89,9 +89,9 @@ export function buildSkillUrl(skill: WebhookSkillPayload, siteUrl: string) {
function buildDescription(event: WebhookEvent, skill: WebhookSkillPayload) {
const summary = (skill.summary ?? '').trim()
if (summary) return truncate(summary, 200)
if (event === 'skill.highlighted') return 'Newly highlighted skill on OpenClaw.'
if (skill.version) return `New version v${skill.version} published on OpenClaw.`
return 'New skill published on OpenClaw.'
if (event === 'skill.highlighted') return 'Newly highlighted skill on ClawHub.'
if (skill.version) return `New version v${skill.version} published on ClawHub.`
return 'New skill published on ClawHub.'
}
function parseBoolean(value?: string) {
+1 -1
View File
@@ -7,7 +7,7 @@ export type SoulSeed = {
}
export const SOUL_SEED_HANDLE = 'seed'
export const SOUL_SEED_DISPLAY_NAME = 'OpenClaw Seed'
export const SOUL_SEED_DISPLAY_NAME = 'ClawHub Seed'
export const SOUL_SEED_KEY = 'seed:souls-v1'
// biome-ignore format: seed payload
+1 -1
View File
@@ -7,7 +7,7 @@ read_when:
# Deploy
OpenClaw is two deployables:
ClawHub is two deployables:
- Web app (TanStack Start) → typically Vercel.
- Convex backend → Convex deployment (serves `/api/...` routes).
+1 -1
View File
@@ -10,7 +10,7 @@ read_when:
## Goals
- Compare any file between two versions.
- Default compare: `latest` vs `previous` (SemVer precedence).
- UX feels native to OpenClaw (theme + typography + motion).
- UX feels native to ClawHub (theme + typography + motion).
- Inline or side-by-side toggle.
- Public access.
+1 -1
View File
@@ -20,7 +20,7 @@ Example (starter):
```json
{
"name": "OpenClaw",
"name": "ClawHub",
"logo": "public/logo.svg",
"navigation": [
{ "group": "Start", "pages": ["docs/README", "docs/quickstart"] },
+3 -3
View File
@@ -1,12 +1,12 @@
---
summary: "OpenClaw spec: skills registry, versioning, vector search, moderation"
summary: "ClawHub spec: skills registry, versioning, vector search, moderation"
read_when:
- Bootstrapping OpenClaw
- Bootstrapping ClawHub
- Implementing schema/auth/search/versioning
- Reviewing API and upload/download flows
---
# OpenClaw — product + implementation spec (v1)
# ClawHub — product + implementation spec (v1)
## Goals
- onlycrabs.ai mode for sharing `SOUL.md` bundles (host-based entry point).
+2 -2
View File
@@ -7,7 +7,7 @@ read_when:
# Telemetry
OpenClaw uses **minimal telemetry** to compute **install counts** (whats actually in use) and to power better sorting/filtering.
ClawHub uses **minimal telemetry** to compute **install counts** (whats actually in use) and to power better sorting/filtering.
This is based on the CLI `clawhub sync` command.
## When telemetry is collected
@@ -70,7 +70,7 @@ This is evaluated lazily (on the next telemetry report) to avoid background jobs
## Transparency + user controls
OpenClaw provides a private “Installed” tab on your own profile:
ClawHub provides a private “Installed” tab on your own profile:
- Shows the exact roots + installed skills we store.
- Includes a **JSON export** view.
+2 -2
View File
@@ -6,7 +6,7 @@ read_when:
# Webhooks (Discord)
OpenClaw can post Discord embeds when skills are published or highlighted.
ClawHub can post Discord embeds when skills are published or highlighted.
## Setup
@@ -44,7 +44,7 @@ Discord receives a JSON payload with a single embed:
{ "name": "Owner", "value": "@owner", "inline": true },
{ "name": "Tags", "value": "latest, discord", "inline": false }
],
"footer": { "text": "OpenClaw" }
"footer": { "text": "ClawHub" }
}
]
}
+1 -1
View File
@@ -43,7 +43,7 @@ test('header menu routes render', async ({ page }) => {
if (label === 'Search') {
await expect(page).toHaveURL(/\/?(\?|$)/)
await expect(page.locator('h1', { hasText: 'OpenClaw' })).toBeVisible()
await expect(page.locator('h1', { hasText: 'ClawHub' })).toBeVisible()
}
}
})
+1 -1
View File
@@ -1,6 +1,6 @@
# `clawhub`
OpenClaw CLI — install, update, search, and publish agent skills as folders.
ClawHub CLI — install, update, search, and publish agent skills as folders.
## Install
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "clawhub",
"version": "0.4.0",
"description": "OpenClaw CLI \\u2014 install, update, search, and publish agent skills.",
"description": "ClawHub CLI \\u2014 install, update, search, and publish agent skills.",
"license": "MIT",
"type": "module",
"bin": {
+1 -1
View File
@@ -60,7 +60,7 @@ describe('browserAuth', () => {
const response = await fetch(server.redirectUri)
expect(response.status).toBe(200)
const text = await response.text()
expect(text).toContain('OpenClaw CLI Login')
expect(text).toContain('ClawHub CLI Login')
server.close()
})
+1 -1
View File
@@ -127,7 +127,7 @@ const CALLBACK_HTML = `<!doctype html>
<html lang="en">
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>OpenClaw CLI Login</title>
<title>ClawHub CLI Login</title>
<style>
:root { color-scheme: light dark; }
body { font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, sans-serif; padding: 24px; }
+1 -1
View File
@@ -20,7 +20,7 @@ import { readGlobalConfig } from './config.js'
const program = new Command()
.name('clawhub')
.description(
`${styleTitle(`OpenClaw CLI ${getCliBuildLabel()}`)}\n${styleEnvBlock(
`${styleTitle(`ClawHub CLI ${getCliBuildLabel()}`)}\n${styleEnvBlock(
'install, update, search, and publish agent skills.',
)}`,
)
+1 -1
View File
@@ -47,7 +47,7 @@ export async function cmdLogin(
) {
if (!tokenFlag && !inputAllowed) fail('Token required (use --token or remove --no-input)')
const token = tokenFlag || (await promptHidden('OpenClaw token: '))
const token = tokenFlag || (await promptHidden('ClawHub token: '))
if (!token) fail('Token required')
const registry = await getRegistry(opts, { cache: true })
+1 -1
View File
@@ -30,7 +30,7 @@ import type { Candidate, LocalSkill, SyncOptions } from './syncTypes.js'
export async function cmdSync(opts: GlobalOpts, options: SyncOptions, inputAllowed: boolean) {
const allowPrompt = isInteractive() && inputAllowed !== false
intro('OpenClaw sync')
intro('ClawHub sync')
const cfg = await readGlobalConfig()
const token = cfg?.token
+1 -1
View File
@@ -1,3 +1,3 @@
# clawhub-schema
Shared runtime schemas (ArkType) for OpenClaw.
Shared runtime schemas (ArkType) for ClawHub.
+1 -1
View File
@@ -1,7 +1,7 @@
{
"openapi": "3.1.0",
"info": {
"title": "OpenClaw API",
"title": "ClawHub API",
"version": "1.0.0",
"description": "Public REST API for skills. Rate limits: read 120/min per IP + 600/min per key; write 30/min per IP + 120/min per key."
},
+1 -1
View File
@@ -76,7 +76,7 @@
fill="#F6EFE4"
font-size="92"
font-weight="700"
font-family="Bricolage Grotesque, Manrope, -apple-system, BlinkMacSystemFont, Helvetica Neue, Helvetica, Arial, sans-serif">OpenClaw</text>
font-family="Bricolage Grotesque, Manrope, -apple-system, BlinkMacSystemFont, Helvetica Neue, Helvetica, Arial, sans-serif">ClawHub</text>
<!-- Subtitle -->
<text x="114" y="332"

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

+2 -2
View File
@@ -116,8 +116,8 @@ function wrapText(value: string, maxWidth: number, fontSize: number, maxLines: n
}
export function buildSkillOgSvg(params: SkillOgSvgParams) {
const rawTitle = params.title.trim() || 'OpenClaw Skill'
const rawDescription = params.description.trim() || 'Published on OpenClaw.'
const rawTitle = params.title.trim() || 'ClawHub Skill'
const rawDescription = params.description.trim() || 'Published on ClawHub.'
const cardX = 72
const cardY = 96
+4 -4
View File
@@ -4,7 +4,7 @@ import { Menu, Monitor, Moon, Sun } from 'lucide-react'
import { useMemo, useRef } from 'react'
import { gravatarUrl } from '../lib/gravatar'
import { isModerator } from '../lib/roles'
import { getOpenClawSiteUrl, getSiteMode, getSiteName } from '../lib/site'
import { getClawHubSiteUrl, getSiteMode, getSiteName } from '../lib/site'
import { applyTheme, useThemeMode } from '../lib/theme'
import { startThemeTransition } from '../lib/theme-transition'
import { useAuthStatus } from '../lib/useAuthStatus'
@@ -25,7 +25,7 @@ export default function Header() {
const siteMode = getSiteMode()
const siteName = useMemo(() => getSiteName(siteMode), [siteMode])
const isSoulMode = siteMode === 'souls'
const clawdHubUrl = getOpenClawSiteUrl()
const clawHubUrl = getClawHubSiteUrl()
const avatar = me?.image ?? (me?.email ? gravatarUrl(me.email) : undefined)
const handle = me?.handle ?? me?.displayName ?? 'user'
@@ -59,7 +59,7 @@ export default function Header() {
<span className="brand-name">{siteName}</span>
</Link>
<nav className="nav-links">
{isSoulMode ? <a href={clawdHubUrl}>OpenClaw</a> : null}
{isSoulMode ? <a href={clawHubUrl}>ClawHub</a> : null}
{isSoulMode ? (
<Link
to="/souls"
@@ -133,7 +133,7 @@ export default function Header() {
<DropdownMenuContent align="end">
{isSoulMode ? (
<DropdownMenuItem asChild>
<a href={clawdHubUrl}>OpenClaw</a>
<a href={clawHubUrl}>ClawHub</a>
</DropdownMenuItem>
) : null}
<DropdownMenuItem asChild>
+3 -3
View File
@@ -14,7 +14,7 @@ describe('og helpers', () => {
summary: 'Forecasts for your area.',
version: '1.2.3',
})
expect(meta.title).toBe('Weather — OpenClaw')
expect(meta.title).toBe('Weather — ClawHub')
expect(meta.description).toBe('Forecasts for your area.')
expect(meta.url).toContain('/steipete/weather')
expect(meta.owner).toBe('steipete')
@@ -48,8 +48,8 @@ describe('og helpers', () => {
it('uses defaults when owner and summary are missing', () => {
const meta = buildSkillMeta({ slug: 'parser' })
expect(meta.title).toBe('parser — OpenClaw')
expect(meta.description).toMatch(/OpenClaw — a fast skill registry/i)
expect(meta.title).toBe('parser — ClawHub')
expect(meta.description).toMatch(/ClawHub — a fast skill registry/i)
expect(meta.url).toContain('/unknown/parser')
expect(meta.owner).toBeNull()
expect(meta.image).toContain('slug=parser')
+5 -5
View File
@@ -1,4 +1,4 @@
import { getOpenClawSiteUrl, getOnlyCrabsSiteUrl } from './site'
import { getOnlyCrabsSiteUrl, getClawHubSiteUrl } from './site'
type SkillMetaSource = {
slug: string
@@ -33,13 +33,13 @@ type SoulMeta = {
owner: string | null
}
const DEFAULT_DESCRIPTION = 'OpenClaw — a fast skill registry for agents, with vector search.'
const DEFAULT_DESCRIPTION = 'ClawHub — a fast skill registry for agents, with vector search.'
const DEFAULT_SOUL_DESCRIPTION = 'SoulHub — the home for SOUL.md bundles and personal system lore.'
const OG_SKILL_IMAGE_LAYOUT_VERSION = '5'
const OG_SOUL_IMAGE_LAYOUT_VERSION = '1'
export function getSiteUrl() {
return getOpenClawSiteUrl()
return getClawHubSiteUrl()
}
export function getSoulSiteUrl() {
@@ -103,9 +103,9 @@ export function buildSkillMeta(source: SkillMetaSource): SkillMeta {
const displayName = clean(source.displayName) || clean(source.slug)
const summary = clean(source.summary)
const version = clean(source.version)
const title = `${displayName}OpenClaw`
const title = `${displayName} — ClawHub`
const description =
summary || (owner ? `Agent skill by @${owner} on OpenClaw.` : DEFAULT_DESCRIPTION)
summary || (owner ? `Agent skill by @${owner} on ClawHub.` : DEFAULT_DESCRIPTION)
const ownerPath = owner || ownerId || 'unknown'
const url = `${siteUrl}/${ownerPath}/${source.slug}`
const imageParams = new URLSearchParams()
+5 -5
View File
@@ -5,9 +5,9 @@ import { afterEach, describe, expect, it, vi } from 'vitest'
import {
detectSiteMode,
detectSiteModeFromUrl,
getOpenClawSiteUrl,
getOnlyCrabsHost,
getOnlyCrabsSiteUrl,
getClawHubSiteUrl,
getSiteDescription,
getSiteMode,
getSiteName,
@@ -42,9 +42,9 @@ afterEach(() => {
describe('site helpers', () => {
it('returns default and env configured site URLs', () => {
expect(getOpenClawSiteUrl()).toBe('https://clawhub.ai')
expect(getClawHubSiteUrl()).toBe('https://clawhub.ai')
withMetaEnv({ VITE_SITE_URL: 'https://example.com' }, () => {
expect(getOpenClawSiteUrl()).toBe('https://example.com')
expect(getClawHubSiteUrl()).toBe('https://example.com')
})
})
@@ -118,10 +118,10 @@ describe('site helpers', () => {
})
it('derives site metadata from mode', () => {
expect(getSiteName('skills')).toBe('OpenClaw')
expect(getSiteName('skills')).toBe('ClawHub')
expect(getSiteName('souls')).toBe('SoulHub')
expect(getSiteDescription('skills')).toContain('OpenClaw')
expect(getSiteDescription('skills')).toContain('ClawHub')
expect(getSiteDescription('souls')).toContain('SoulHub')
expect(getSiteUrlForMode('skills')).toBe('https://clawhub.ai')
+4 -4
View File
@@ -4,7 +4,7 @@ const DEFAULT_CLAWHUB_SITE_URL = 'https://clawhub.ai'
const DEFAULT_ONLYCRABS_SITE_URL = 'https://onlycrabs.ai'
const DEFAULT_ONLYCRABS_HOST = 'onlycrabs.ai'
export function getOpenClawSiteUrl() {
export function getClawHubSiteUrl() {
return import.meta.env.VITE_SITE_URL ?? DEFAULT_CLAWHUB_SITE_URL
}
@@ -70,15 +70,15 @@ export function getSiteMode(): SiteMode {
}
export function getSiteName(mode: SiteMode = getSiteMode()) {
return mode === 'souls' ? 'SoulHub' : 'OpenClaw'
return mode === 'souls' ? 'SoulHub' : 'ClawHub'
}
export function getSiteDescription(mode: SiteMode = getSiteMode()) {
return mode === 'souls'
? 'SoulHub — the home for SOUL.md bundles and personal system lore.'
: 'OpenClaw — a fast skill registry for agents, with vector search.'
: 'ClawHub — a fast skill registry for agents, with vector search.'
}
export function getSiteUrlForMode(mode: SiteMode = getSiteMode()) {
return mode === 'souls' ? getOnlyCrabsSiteUrl() : getOpenClawSiteUrl()
return mode === 'souls' ? getOnlyCrabsSiteUrl() : getClawHubSiteUrl()
}
+1 -1
View File
@@ -32,7 +32,7 @@ function SkillsHome() {
<div className="hero-inner">
<div className="hero-copy fade-up" data-delay="1">
<span className="hero-badge">Lobster-light. Agent-right.</span>
<h1 className="hero-title">OpenClaw, the skill dock for sharp agents.</h1>
<h1 className="hero-title">ClawHub, the skill dock for sharp agents.</h1>
<p className="hero-subtitle">
Upload AgentSkills bundles, version them like npm, and make them searchable with
vectors. No gatekeeping, just signal.