mirror of
https://github.com/openclaw/clawhub.git
synced 2026-08-14 00:47:57 +00:00
fix: use ClawHub branding for registry
This commit is contained in:
+2
-2
@@ -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).
|
||||
|
||||
|
||||
@@ -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).
|
||||
It’s 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.
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -7,7 +7,7 @@ read_when:
|
||||
|
||||
# Telemetry
|
||||
|
||||
OpenClaw uses **minimal telemetry** to compute **install counts** (what’s actually in use) and to power better sorting/filtering.
|
||||
ClawHub uses **minimal telemetry** to compute **install counts** (what’s 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
@@ -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" }
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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,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,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": {
|
||||
|
||||
@@ -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()
|
||||
})
|
||||
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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.',
|
||||
)}`,
|
||||
)
|
||||
|
||||
@@ -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 })
|
||||
|
||||
@@ -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,3 +1,3 @@
|
||||
# clawhub-schema
|
||||
|
||||
Shared runtime schemas (ArkType) for OpenClaw.
|
||||
Shared runtime schemas (ArkType) for ClawHub.
|
||||
|
||||
@@ -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
@@ -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 |
@@ -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,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
@@ -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
@@ -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,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
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user