From 77c7bd556b841306ace632cdcb024d554ff0a29e Mon Sep 17 00:00:00 2001 From: Aaron Stannard Date: Wed, 22 Jul 2026 03:23:11 +0000 Subject: [PATCH] docs(skills): add skillserver CLI reference, rewrite Skill Server for 0.4.0 Group 2 of the skill-server 0.4.0 epic (#95). Both pages were built against a running 0.4.0 server and CLI, not the specs. - skills/skillserver-cli.md (new): full command reference - publish, publish-all, publish-subagent(s), download-subagent, list(-subagents), versions, verify, lint/lint subagent(s), delete(-subagent), config, api-key; global flags; config precedence; exit codes (0/1 only). - skills/skill-server.md (rewrite): API tables corrected to the real /api/v1/skills, /api/v1/subagents, /api/v1/api-keys, and /api/v1/blobs routes plus the /skills/v1 and /subagents/v1 manifest trees (the old /skills and /api-keys routes are now the web gallery). Adds the sub-agent API and publishing, archive.zip and resources routes, a feeds/native-manifest section, and a web-gallery note. The Docker Compose quick-start now runs the server non-root against a writable volume (verified: healthy, uid 1654, no SQLite error). Sidebar: skillserver CLI added under Skills, after Skill Server. Closes #98, #99. --- astro.config.mjs | 1 + social-images-manifest.json | 8 +- src/content/docs/skills/skill-server.md | 276 ++++++++------------- src/content/docs/skills/skillserver-cli.md | 157 ++++++++++++ 4 files changed, 271 insertions(+), 171 deletions(-) create mode 100644 src/content/docs/skills/skillserver-cli.md diff --git a/astro.config.mjs b/astro.config.mjs index b887ac8..1e01f94 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -71,6 +71,7 @@ export default defineConfig({ { label: 'Native Manifest & Sync', slug: 'skills/native-manifest' }, { label: 'Bundling Resources with Skills', slug: 'skills/bundled-resources' }, { label: 'Skill Server', slug: 'skills/skill-server' }, + { label: 'skillserver CLI', slug: 'skills/skillserver-cli' }, { label: 'External Skills', slug: 'skills/external-skills' }, { label: 'Skill Feeds', slug: 'skills/skill-feeds' }, ], diff --git a/social-images-manifest.json b/social-images-manifest.json index 9dab948..9b8de86 100644 --- a/social-images-manifest.json +++ b/social-images-manifest.json @@ -173,8 +173,8 @@ "imageUrl": "https://hcti.io/v1/image/c970328302630c63745ae3527584ea7145a2450d6305dad2eac6dd53f9e0f704" }, "skills/skill-server": { - "hash": "13d89c9f77cf6820f7e9723f0a1724558f9998349c2f3f44df23a1702f39aede", - "imageUrl": "https://hcti.io/v1/image/a70897afbe3e94297ca1b473ce9ae09db69ed6454338e9722823030203882b81" + "hash": "79847785f7c901f6d57dfa4f48cb11f1e33304708f1a2ae29d34877fa0a4c91f", + "imageUrl": "https://hcti.io/v1/image/2aa2af5666a4d8bbf5c231ac363157f38d50f88821a13b65ef438f143ebf5ce3" }, "__homepage__": { "hash": "832aa2ff01870a7200516422a9855629d4c00d98a9bf6cde5ec3174580750318", @@ -255,6 +255,10 @@ "skills/native-manifest": { "hash": "d420501a6c5bbcc2593deb9fc57fba6fd3df695281aeb585bd7e6e29e83ed8f0", "imageUrl": "https://hcti.io/v1/image/eb22b7288dcc2e2e997fcd389ba65f0032ee0830fa553481f3648eebdc3bd0bb" + }, + "skills/skillserver-cli": { + "hash": "9a89d035d07a361e3b0efdc5020daa190bcd3d654805970e6b54e98da3d8ff97", + "imageUrl": "https://hcti.io/v1/image/f237b21ac6504b3f12cae4d56bc2c708c63d29560f7863d455f5a8389bf935b4" } } } diff --git a/src/content/docs/skills/skill-server.md b/src/content/docs/skills/skill-server.md index 320f319..2b04536 100644 --- a/src/content/docs/skills/skill-server.md +++ b/src/content/docs/skills/skill-server.md @@ -1,25 +1,28 @@ --- title: "Skill Server" -description: "Running and connecting a skill server." +description: "Running and connecting a self-hosted skill server for skills and sub-agents." --- -SkillServer is a self-hosted skill registry — a private NuGet feed or npm registry, but for [SKILL.md](https://agentskills.io) files. Host it behind your firewall, publish proprietary skills, and netclaw instances on the network sync from it automatically. +SkillServer is a self-hosted skill registry - a private NuGet feed or npm registry, but for [SKILL.md](https://agentskills.io) files and netclaw sub-agents. Host it behind your firewall, publish proprietary skills and sub-agents, and every netclaw instance on the network syncs from it automatically. -**Source code and releases: [github.com/netclaw-dev/skill-server](https://github.com/netclaw-dev/skill-server)** +Source code and releases: [github.com/netclaw-dev/skill-server](https://github.com/netclaw-dev/skill-server) -It implements two open standards: +It implements two open standards, plus a native feed for netclaw-aware clients: -- **[Cloudflare Agent Skills Discovery RFC v0.2.0](https://github.com/cloudflare/agent-skills-discovery-rfc)** — discovery via `/.well-known/agent-skills/index.json` -- **[AgentSkills.io](https://agentskills.io)** — the SKILL.md format for skill definitions +- [Cloudflare Agent Skills Discovery RFC v0.2.0](https://github.com/cloudflare/agent-skills-discovery-rfc) - discovery via `/.well-known/agent-skills/index.json` +- [AgentSkills.io](https://agentskills.io) - the SKILL.md format for skill definitions +- [Native Manifest](/skills/native-manifest/) - a versioned `/manifest.json` feed that also carries sub-agents and packaged archives -Any agent that supports these standards can consume skills from your server, not just netclaw. +Any agent that speaks the RFC can consume skills from your server, and netclaw additionally reads the native manifest for sub-agents and archive artifacts. ## What it does -- Stores versioned skills in content-addressable blob storage (SHA-256) -- Serves a discovery index that agents poll on an interval -- API key auth on writes; reads are open (agents fetch skills without credentials) -- Single container, no external dependencies — just SQLite + filesystem +- Stores versioned skills and sub-agents in content-addressable blob storage (SHA-256) +- Serves an RFC discovery index and a native manifest that agents poll on an interval +- Packages skills with bundled resources as deterministic [archives](/skills/bundled-resources/) +- Ships a web gallery for browsing skills and sub-agents +- Requires API key auth on writes. Reads are open, so agents fetch without credentials +- Runs as a single container with no external dependencies, just SQLite and the filesystem ## Deploy with Docker @@ -34,9 +37,25 @@ Available for `linux/amd64` and `linux/arm64`. ### Docker Compose ```yaml +name: skillserver + services: + # The server runs as a non-root user (uid 1654). Docker creates the named + # volume's mount point owned by root, so this one-shot service fixes ownership + # before the server starts - otherwise it can't create the SQLite database. + init-data: + image: ghcr.io/netclaw-dev/skillserver:latest + user: "0:0" + volumes: + - skill-data:/data + entrypoint: ["sh", "-c", "chown -R 1654:1654 /data"] + restart: "no" + skill-server: image: ghcr.io/netclaw-dev/skillserver:latest + depends_on: + init-data: + condition: service_completed_successfully ports: - "8080:8080" volumes: @@ -65,7 +84,7 @@ Verify it's running: curl http://localhost:8080/health ``` -The bootstrap API key is hashed and stored on first startup. Save the raw value — it cannot be recovered from the server. +The bootstrap API key is hashed and stored on first startup. Save the raw value, because it can't be recovered from the server. ## Configuration @@ -84,7 +103,7 @@ All state lives in `SKILLSERVER__DATAPATH`. Back up that volume and you have eve - Put a reverse proxy (Caddy, nginx, Traefik) in front for TLS - Set `SKILLSERVER__BASEURL` to your public URL (e.g., `https://skills.internal.example.com`) so discovery responses have correct absolute URLs -- Mount `/data` to persistent storage — if the volume is lost, you'll need to re-publish all skills +- Mount `/data` to persistent storage. If the volume is lost, you'll re-publish everything ## API key management @@ -92,172 +111,65 @@ Reads are open. Writes (publish, delete, key management) require a `Bearer` toke ### Bootstrap -Set `SKILLSERVER__APIKEY` before the first run. The server hashes it and stores it as the "bootstrap" key. Once any key exists in the database, this environment variable is ignored on subsequent starts. +Set `SKILLSERVER__APIKEY` before the first run. The server hashes it and stores it as the "bootstrap" key. Once any key exists in the database, this environment variable is ignored on later starts. ### Create additional keys ```bash -curl -X POST http://localhost:8080/api-keys \ +curl -X POST http://localhost:8080/api/v1/api-keys \ -H "Authorization: Bearer sk-your-bootstrap-key" \ -H "Content-Type: application/json" \ -d '{"label": "ci-deploy"}' ``` -The response contains the raw key once. Store it in a secret manager or password vault. +The response contains the raw key once. Store it in a secret manager or password vault. The [`skillserver api-key`](/skills/skillserver-cli/) commands do the same thing without hand-writing curl. ### List and revoke ```bash # List (never shows raw keys) -curl http://localhost:8080/api-keys \ +curl http://localhost:8080/api/v1/api-keys \ -H "Authorization: Bearer sk-your-key" # Revoke -curl -X DELETE http://localhost:8080/api-keys/2 \ +curl -X DELETE http://localhost:8080/api/v1/api-keys/2 \ -H "Authorization: Bearer sk-your-key" ``` -You cannot delete the last remaining key. +You can't delete the last remaining key. ### Key format -Format: `sk-{random}` (256 bits entropy, base64url-encoded). Stored as SHA-256 hashes, compared in constant time. Raw keys never touch disk. - -## Publishing skills (skillserver CLI) +`sk-{random}` (256 bits of entropy, base64url-encoded). Stored as SHA-256 hashes, compared in constant time. Raw keys never touch disk. -The `skillserver` CLI handles publishing and management from your terminal or CI. +## Publishing skills and sub-agents -### Install +The [`skillserver` CLI](/skills/skillserver-cli/) handles publishing and management from your terminal or CI, and that page has the full command and flag reference. The essentials: ```bash -# Standalone binary (no .NET runtime required) -curl -fsSL https://raw.githubusercontent.com/netclaw-dev/skill-server/dev/scripts/install-skillserver.sh | bash - -# Or as a .NET global tool -dotnet tool install --global Netclaw.SkillServer.Cli -``` - -Standalone binaries are available for linux-x64, linux-arm64, osx-arm64, and win-x64. - -### Configure - -```bash -skillserver config init -``` - -Prompts for server URL and API key. Saves to `~/.skillserver/config.json`. - -For CI, use environment variables instead: - -```bash -export SKILLSERVER_URL=https://skills.internal.example.com -export SKILLSERVER_API_KEY=sk-your-ci-key -``` - -Priority: CLI flags > environment variables > config file. - -### Publish - -A skill is a directory containing a `SKILL.md` with YAML frontmatter: - -``` -my-skill/ - SKILL.md # Required — name, description, version in frontmatter - resources/ # Optional — supporting files -``` - -Name validation: 1-64 chars, lowercase alphanumeric + hyphens (`^[a-z0-9]+(-[a-z0-9]+)*$`). - -```bash -# Single skill +# Publish one skill, or every skill directory under a parent skillserver publish ./my-skill - -# All skills in a directory skillserver publish-all ./skills -# Dry run (validates without uploading) -skillserver publish-all ./skills --dry-run - -# Force re-publish (deletes existing version first) -skillserver publish ./my-skill --force -``` - -Publishing a version that already exists returns 409 Conflict and the CLI skips it. This makes `publish-all` idempotent — safe to run repeatedly in CI. - -### Other commands - -```bash -# List and search -skillserver list -skillserver list --search kubernetes - -# Version history -skillserver versions my-skill - -# Verify local matches server -skillserver verify ./my-skill - -# Delete a version -skillserver delete my-skill 1.0.0 --yes - -# Manage API keys -skillserver api-key create --label "CI Pipeline" -skillserver api-key list -skillserver api-key delete 7 +# Publish sub-agents (version is required - it's not read from frontmatter) +skillserver publish-subagent ./release-notes-writer.md --version 1.0.0 +skillserver publish-subagents ./subagents ``` -### Global options +Publishing a version that already exists is skipped, so `publish-all` and `publish-subagents` are idempotent and safe to run on every CI push. A skill with a `references/`, `scripts/`, or `assets/` folder is packaged as an [archive](/skills/bundled-resources/) that preserves relative paths and executable bits. A bare `SKILL.md` publishes as a lightweight skill-md artifact. -| Option | Description | -|--------|-------------| -| `--server-url ` | Override server URL | -| `--api-key ` | Override API key | -| `--output text\|json` | Output format | -| `--verbose, -v` | Show HTTP request/response details | + -## CI/CD integration +## Feeds and the native manifest -A GitHub Actions workflow that publishes on push and validates on PR. This example uses Tailscale to reach a server on a private network: +The server exposes two discovery feeds: -```yaml -name: Publish Skills -on: - push: - branches: [master] - pull_request: - branches: [master] - -jobs: - publish: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - name: Connect to Tailscale - uses: tailscale/github-action@v2 - with: - oauth-client-id: ${{ secrets.TS_OAUTH_CLIENT_ID }} - oauth-secret: ${{ secrets.TS_OAUTH_SECRET }} - - - name: Install skillserver CLI - run: curl -fsSL https://raw.githubusercontent.com/netclaw-dev/skill-server/dev/scripts/install-skillserver.sh | bash - - - name: Publish skills - if: github.event_name == 'push' - env: - SKILLSERVER_URL: ${{ secrets.SKILLSERVER_URL }} - SKILLSERVER_API_KEY: ${{ secrets.SKILLSERVER_API_KEY }} - run: skillserver publish-all ./skills - - - name: Validate skills (PRs only) - if: github.event_name == 'pull_request' - env: - SKILLSERVER_URL: ${{ secrets.SKILLSERVER_URL }} - SKILLSERVER_API_KEY: ${{ secrets.SKILLSERVER_API_KEY }} - run: skillserver publish-all ./skills --dry-run -``` +| Feed | Path | Contents | +|------|------|----------| +| RFC index | `/.well-known/agent-skills/index.json` | Skills only, per the Cloudflare RFC | +| Native manifest | `/manifest.json` | Skills, sub-agents, and archives, with API version negotiation | -PR builds validate with `--dry-run`. Pushes to master publish for real. Existing versions return 409 and are skipped — only new or bumped versions actually upload. +The netclaw daemon reads the native manifest, so it picks up sub-agents and archive resources the RFC feed can't express. See [Native Manifest](/skills/native-manifest/) for the manifest tree and version negotiation, and [Skill Feeds](/skills/skill-feeds/) for how the daemon syncs from it. ## Connecting netclaw instances @@ -267,50 +179,76 @@ Add a skill server as a feed source through `netclaw config` → Skill Sources ( { "SkillFeeds": { "Feeds": [ { "Name": "my-server", "Url": "http://skills.internal.example.com", "Enabled": true } ] } } ``` -The daemon syncs on a periodic interval. Skills land in `~/.netclaw/skills/.server-feeds/` (read-only). See [Skill Feeds](/skills/skill-feeds/) for sync intervals, authentication, and selective sync options. +The daemon syncs on a periodic interval. Skills land in `~/.netclaw/skills/.server-feeds/` and sub-agents in `~/.netclaw/agents/.server-feeds/`, both read-only. See [Skill Feeds](/skills/skill-feeds/) for sync intervals, authentication, and selective sync options. ## API reference -### Discovery (no auth) +Reads are open. Endpoints marked `auth` require an `Authorization: Bearer ` header. + +### Discovery and feeds | Method | Path | Description | |--------|------|-------------| -| GET | `/.well-known/agent-skills/index.json` | RFC-compliant skill discovery index | -| GET | `/manifest.json` | NetClaw-compatible manifest | | GET | `/health` | Health check | +| GET | `/api/v1/info` | Server info and capabilities | +| GET | `/.well-known/agent-skills/index.json` | RFC-compliant skill discovery index | +| GET | `/manifest.json` | Native manifest root (version negotiation) | +| GET | `/skills/v1/index.json` | Native skill collection (paginated) | +| GET | `/skills/v1/{name}/versions/{version}.json` | Native skill version entry | +| GET | `/subagents/v1/index.json` | Native sub-agent collection (paginated) | +| GET | `/subagents/v1/{name}/versions/{version}.json` | Native sub-agent version entry | -### Skills (reads open, writes require auth) +### Skills | Method | Path | Description | |--------|------|-------------| -| GET | `/skills` | List all (`?q=`, `?skip=`, `?take=` supported) | -| GET | `/skills/{name}` | All versions of a skill | -| GET | `/skills/{name}/latest` | Latest version metadata | -| GET | `/skills/{name}/{version}` | Specific version metadata | -| GET | `/skills/{name}/{version}/SKILL.md` | Download skill content | -| GET | `/skills/{name}/{version}/{*path}` | Download resource files | -| POST | `/skills/check-updates` | Batch update check (up to 100 skills) | -| POST | `/skills` | Upload new version (multipart/form-data) | -| DELETE | `/skills/{name}/{version}` | Delete a version | - -### API keys (all require auth) +| GET | `/api/v1/skills` | List all (`?q=`, `?skip=`, `?take=` supported) | +| GET | `/api/v1/skills/{name}` | All versions of a skill | +| GET | `/api/v1/skills/{name}/latest` | Latest version metadata | +| GET | `/api/v1/skills/{name}/{version}` | Specific version metadata | +| GET | `/api/v1/skills/{name}/{version}/SKILL.md` | Download SKILL.md | +| GET | `/api/v1/skills/{name}/{version}/archive.zip` | Download the packaged archive | +| GET | `/api/v1/skills/{name}/{version}/resources` | List bundled resources (path, digest, mode) | +| GET | `/api/v1/skills/{name}/{version}/{*path}` | Download a single resource file | +| POST | `/api/v1/skills/check-updates` | Batch update check | +| POST | `/api/v1/skills` | `auth` Upload a new version (multipart/form-data) | +| DELETE | `/api/v1/skills/{name}/{version}` | `auth` Delete a version | + +### Sub-agents | Method | Path | Description | |--------|------|-------------| -| POST | `/api-keys` | Create a key (returns raw key once) | -| GET | `/api-keys` | List keys (hashed, never shows raw) | -| DELETE | `/api-keys/{id}` | Revoke a key | +| GET | `/api/v1/subagents` | List all sub-agents | +| GET | `/api/v1/subagents/{name}` | All versions of a sub-agent | +| GET | `/api/v1/subagents/{name}/{version}` | Specific version metadata | +| GET | `/api/v1/subagents/{name}/{version}/agent.md` | Download the sub-agent definition | +| POST | `/api/v1/subagents` | `auth` Upload a new version | +| DELETE | `/api/v1/subagents/{name}/{version}` | `auth` Delete a version | + +### Blobs and API keys + +| Method | Path | Description | +|--------|------|-------------| +| GET | `/api/v1/blobs/sha256/{digest}` | Download a blob by digest | +| POST | `/api/v1/api-keys` | `auth` Create a key (returns raw key once) | +| GET | `/api/v1/api-keys` | `auth` List keys (hashed, never shows raw) | +| DELETE | `/api/v1/api-keys/{id}` | `auth` Revoke a key | + +### Web gallery + +The server also serves a browser UI: `/`, `/skills`, and `/subagents` return the gallery SPA rather than JSON. ## Related pages -- [Skills Overview](/skills/overview/) — format, lifecycle, source types -- [Skill Feeds](/skills/skill-feeds/) — configuring netclaw to consume from skill servers -- [External Skills](/skills/external-skills/) — local skill directories -- [Security Model](/security/security-model/) — content scanning and trust model +- [skillserver CLI](/skills/skillserver-cli/) - full publishing and management command reference +- [Native Manifest](/skills/native-manifest/) - the sync feed netclaw reads for skills and sub-agents +- [Bundling Resources with Skills](/skills/bundled-resources/) - archive artifacts and packaged resources +- [Skill Feeds](/skills/skill-feeds/) - configuring netclaw to consume from skill servers +- [Skills Overview](/skills/overview/) - format, lifecycle, source types +- [Security Model](/security/security-model/) - content scanning and trust model ## External resources -- [AgentSkills.io](https://agentskills.io) — the SKILL.md format spec -- [Cloudflare Agent Skills Discovery RFC](https://github.com/cloudflare/agent-skills-discovery-rfc) — the discovery protocol -- [netclaw-dev/skill-server on GitHub](https://github.com/netclaw-dev/skill-server) — source, issues, releases -- [Tailscale GitHub Action](https://github.com/tailscale/github-action) — CI/CD access to private network servers +- [AgentSkills.io](https://agentskills.io) - the SKILL.md format spec +- [Cloudflare Agent Skills Discovery RFC](https://github.com/cloudflare/agent-skills-discovery-rfc) - the discovery protocol +- [netclaw-dev/skill-server on GitHub](https://github.com/netclaw-dev/skill-server) - source, issues, releases diff --git a/src/content/docs/skills/skillserver-cli.md b/src/content/docs/skills/skillserver-cli.md new file mode 100644 index 0000000..3625f7c --- /dev/null +++ b/src/content/docs/skills/skillserver-cli.md @@ -0,0 +1,157 @@ +--- +title: "skillserver CLI" +description: "Publish, validate, and manage skills and sub-agents on a skill server from the command line." +--- + +`skillserver` is the command-line tool for publishing and managing skills and sub-agents on a [skill server](/skills/skill-server/). It's what you run by hand during authoring and what your [CI pipeline](/skills/skill-server/) runs on merge. It speaks to the server's REST API, so anything the [gallery](/skills/skill-server/) shows, `skillserver` can do from a terminal. + +## Install + +```bash +# .NET global tool +dotnet tool install --global Netclaw.SkillServer.Cli + +# Or a self-contained binary (Linux/macOS), no .NET runtime needed +curl -fsSL https://raw.githubusercontent.com/netclaw-dev/skill-server/dev/scripts/install-skillserver.sh | bash +``` + +For CI, pin the version. The install script takes one as an argument (`bash -s -- 0.4.0`), or commit a `dotnet-tools.json` manifest and run `dotnet tool restore`. + +## Configure + +`skillserver` needs a server URL, and a publish API key for any command that writes. It resolves both from three sources, highest priority first: + +| Source | How to set | Use for | +|--------|-----------|---------| +| CLI flags | `--server-url`, `--api-key` | One-off overrides | +| Environment | `SKILLSERVER_URL`, `SKILLSERVER_API_KEY` | CI | +| Config file | `skillserver config init` → `~/.skillserver/config.json` | Local dev | + +```bash +skillserver config init # interactive first-run setup +skillserver config set server-url https://skills.example.com +skillserver config show # print current config (no secrets) +``` + +Read-only commands (`list`, `list-subagents`, `versions`, `verify`, `download-subagent`) need only the URL. Everything that writes needs a key. + +## Commands + +### Publish + +```bash +skillserver publish ./my-skill # one skill; version from SKILL.md frontmatter +skillserver publish ./my-skill --version 2.0.0 # override the version +skillserver publish-all ./skills # every skill dir under ./skills +skillserver publish-subagent ./agent.md --version 1.0.0 +skillserver publish-subagents ./subagents # every .md under ./subagents +``` + +`publish` points at a single skill directory (the one holding `SKILL.md`). `publish-all` points at the *parent* directory and publishes each subdirectory. Both skip versions already on the server unless you pass `--force`. Sub-agents take their version from `--version`, which is required. The markdown frontmatter's `metadata.version` is ignored. + +```text +$ skillserver publish-all ./skills +Scanning ./skills... +Found 2 skill(s) to publish. + + hello-greeter@1.0.0 Published + k8s-log-triage@0.2.0 Published + +Results: 2 published, 0 skipped, 0 failed +``` + +| Flag | Meaning | +|------|---------| +| `--version ` | Override the version (required for sub-agents) | +| `--force`, `-f` | Delete the existing version, then re-publish | +| `--dry-run` | Show what would publish without uploading | + +### Inspect and download + +```bash +skillserver list # all skills: name, latest, version count +skillserver list --search kubernetes # full-text search +skillserver versions my-skill # every version of one skill +skillserver list-subagents +skillserver verify ./my-skill # local files vs published digests +skillserver download-subagent release-notes-writer 1.0.0 ./agent.md +``` + +`verify` hashes each local file and compares it to the published version, which is handy in CI to confirm a release matches the repo: + +```text +$ skillserver verify ./k8s-log-triage +Verifying k8s-log-triage@0.2.0... + SKILL.md match + scripts/collect-logs.sh match + references/kubectl-cheatsheet.md match + assets/triage-template.json match +All files verified +``` + +### Validate (no server needed) + +`lint` runs entirely on local files, with no URL and no key, so it's the natural PR gate. + +```bash +skillserver lint ./skills # every skill dir under ./skills +skillserver lint subagent ./agent.md # one sub-agent file +skillserver lint subagents ./subagents # every sub-agent .md in a dir +``` + +It checks that frontmatter parses, required fields are present, names are lowercase-kebab, and versions are semver. + +:::caution +`lint` only exits non-zero on hard errors: a missing `SKILL.md`, a missing required field, or unparseable frontmatter. Softer problems are warnings that still exit 0, including a `name` that doesn't match its directory and a version that isn't valid semver. A name/directory mismatch is the number one publishing mistake, and `lint` won't fail CI over it, so read the warning output instead of trusting the exit code alone. +::: + +### Manage + +```bash +skillserver delete my-skill 1.0.0 --yes +skillserver delete-subagent release-notes-writer 1.0.0 --yes +skillserver api-key create --label ci-publish # prints the secret ONCE +skillserver api-key list # ids + labels, never secrets +skillserver api-key delete 2 +``` + +`--yes`/`-y` skips the delete confirmation prompt, so reach for it in scripts. `api-key create` shows the `sk-…` secret a single time, so store it immediately. + +## Global flags + +| Flag | Description | +|------|-------------| +| `--server-url ` | Override the configured URL | +| `--api-key ` | Override the configured key | +| `--output ` | Output format (default `text`) | +| `--verbose`, `-v` | Detailed progress / upload logs | +| `--help`, `-h` | Command help (works without a server) | +| `--version` | Print the CLI version | + +Pass `--output json` to any read command for scripting: + +```bash +skillserver list --output json | jq '.[].name' +``` + +## Exit codes + +| Code | Meaning | +|------|---------| +| `0` | Success | +| `1` | Any failure: usage error, unknown command, validation error, auth failure, or a rejected server request | + +There's no separate code for usage versus runtime errors. Both are `1`, so scripts should branch on `0` versus non-zero. + +## Related pages + +- [Skill Server](/skills/skill-server/) - run the server this CLI talks to. +- [Bundling Resources with Skills](/skills/bundled-resources/) - what `publish` packages when a skill has `references/`, `scripts/`, or `assets/`. +- [Native Manifest](/skills/native-manifest/) - the sync feed a netclaw daemon reads from the server. +- [Skill Feeds](/skills/skill-feeds/) - point a netclaw daemon at the published feed. + +## Resources + +- [skillserver CLI README](https://github.com/netclaw-dev/skill-server/blob/dev/src/Netclaw.SkillServer.Cli/README.md) - upstream reference. +- [AgentSkills.io](https://agentskills.io) - the `SKILL.md` format `lint` validates against. +- [.NET local tools](https://learn.microsoft.com/en-us/dotnet/core/tools/local-tools-how-to-use) - pinning the CLI with `dotnet-tools.json`.