feat: add skill metadata docs, suspicious appeal banner for owners

- Document full frontmatter metadata reference in docs/skill-format.md
- Add metadata section + quick example to README
- Show appeal message on suspicious skills (owner-only) linking to GitHub issues
- Accept metadata.openclaw alias in README docs
- Re-evaluate all skills with full file content reading (backfill in progress)
This commit is contained in:
theonejvo
2026-02-11 18:36:45 +11:00
parent 3e39651074
commit 3402f0e735
6 changed files with 141 additions and 5 deletions
+24 -1
View File
@@ -138,7 +138,30 @@ metadata: {"clawdbot":{"cliHelp":"padel --help\\nUsage: padel [command]\\n"}}
---
```
`metadata.clawdbot` is preferred, but `metadata.clawdis` is accepted as an alias for compatibility.
`metadata.clawdbot` is preferred, but `metadata.clawdis` and `metadata.openclaw` are accepted as aliases.
## Skill metadata
Skills declare their runtime requirements (env vars, binaries, install specs) in the `SKILL.md` frontmatter. ClawHub's security analysis checks these declarations against actual skill behavior.
Full reference: [`docs/skill-format.md`](docs/skill-format.md#frontmatter-metadata)
Quick example:
```yaml
---
name: my-skill
description: Does a thing with an API.
metadata:
openclaw:
requires:
env:
- MY_API_KEY
bins:
- curl
primaryEnv: MY_API_KEY
---
```
## Scripts
+2 -2
View File
@@ -344,12 +344,12 @@ export const backfillLlmEval = internalAction({
)
for (const { versionId, slug } of batch.skills) {
// The query already filters out versions with llmAnalysis, but double-check
// Re-evaluate all (full file content reading upgrade)
const version = (await ctx.runQuery(internal.skills.getVersionByIdInternal, {
versionId,
})) as Doc<'skillVersions'> | null
if (!version || (version.llmAnalysis && version.llmAnalysis.status !== 'error')) {
if (!version) {
accSkipped++
continue
}
+2 -2
View File
@@ -1678,8 +1678,8 @@ export const getActiveSkillBatchForLlmBackfillInternal = internalQuery({
const version = await ctx.db.get(skill.latestVersionId)
if (!version) continue
// Skip versions that already have a successful llmAnalysis (retry errors)
if (version.llmAnalysis && version.llmAnalysis.status !== 'error') continue
// Re-evaluate all skills (full file content reading upgrade)
// if (version.llmAnalysis && version.llmAnalysis.status !== 'error') continue
results.push({
skillId: skill._id,
+93
View File
@@ -35,6 +35,99 @@ Workdir install state (written by the CLI):
- The server extracts metadata from frontmatter during publish.
- `description` is used as the skill summary in the UI/search.
## Frontmatter metadata
Skill metadata is declared in the YAML frontmatter at the top of your `SKILL.md`. This tells the registry (and security analysis) what your skill needs to run.
### Basic frontmatter
```yaml
---
name: my-skill
description: Short summary of what this skill does.
version: 1.0.0
---
```
### Runtime metadata (`metadata.openclaw`)
Declare your skill's runtime requirements under `metadata.openclaw` (aliases: `metadata.clawdbot`, `metadata.clawdis`).
```yaml
---
name: my-skill
description: Manage tasks via the Todoist API.
metadata:
openclaw:
requires:
env:
- TODOIST_API_KEY
bins:
- curl
primaryEnv: TODOIST_API_KEY
---
```
### Full field reference
| Field | Type | Description |
|-------|------|-------------|
| `requires.env` | `string[]` | Environment variables your skill expects. |
| `requires.bins` | `string[]` | CLI binaries that must all be installed. |
| `requires.anyBins` | `string[]` | CLI binaries where at least one must exist. |
| `requires.config` | `string[]` | Config file paths your skill reads. |
| `primaryEnv` | `string` | The main credential env var for your skill. |
| `always` | `boolean` | If `true`, skill is always active (no explicit install needed). |
| `skillKey` | `string` | Override the skill's invocation key. |
| `emoji` | `string` | Display emoji for the skill. |
| `homepage` | `string` | URL to the skill's homepage or docs. |
| `os` | `string[]` | OS restrictions (e.g. `["macos"]`, `["linux"]`). |
| `install` | `array` | Install specs for dependencies (see below). |
| `nix` | `object` | Nix plugin spec (see README). |
| `config` | `object` | Clawdbot config spec (see README). |
### Install specs
If your skill needs dependencies installed, declare them in the `install` array:
```yaml
metadata:
openclaw:
install:
- kind: brew
formula: jq
bins: [jq]
- kind: node
package: typescript
bins: [tsc]
```
Supported install kinds: `brew`, `node`, `go`, `uv`.
### Why this matters
ClawHub's security analysis checks that what your skill declares matches what it actually does. If your code references `TODOIST_API_KEY` but your frontmatter doesn't declare it under `requires.env`, the analysis will flag a metadata mismatch. Keeping declarations accurate helps your skill pass review and helps users understand what they're installing.
### Example: complete frontmatter
```yaml
---
name: todoist-cli
description: Manage Todoist tasks, projects, and labels from the command line.
version: 1.2.0
metadata:
openclaw:
requires:
env:
- TODOIST_API_KEY
bins:
- curl
primaryEnv: TODOIST_API_KEY
emoji: "\u2705"
homepage: https://github.com/example/todoist-cli
---
```
## Allowed files
Only “text-based” files are accepted by publish.
+9
View File
@@ -577,6 +577,15 @@ export function SkillDetailPage({
ClawHub Security flagged this skill as suspicious. Review the scan results before
using.
</p>
{canManage ? (
<p className="pending-banner-appeal">
If you believe this skill has been incorrectly flagged, please{' '}
<a href="https://github.com/openclaw/clawhub/issues" target="_blank" rel="noopener noreferrer">
submit an issue on GitHub
</a>{' '}
and we'll break down why it was flagged and what you can do.
</p>
) : null}
</div>
</div>
) : modInfo?.isRemoved ? (
+11
View File
@@ -3206,6 +3206,17 @@ html.theme-transition::view-transition-new(theme) {
margin: 0;
}
.pending-banner-appeal {
margin-top: 6px !important;
font-size: 0.8rem !important;
opacity: 0.75;
}
.pending-banner-appeal a {
color: inherit;
text-decoration: underline;
}
/* Blocked/removed banner variant */
.pending-banner-blocked {
background: rgba(239, 68, 68, 0.12);