@skillguard/cli
v3.1.3
Published
Security scanner for AI agent skill files
Maintainers
Readme
@skillguard/cli
Security scanner CLI for AI agent SKILL.md files.
Quick start
npx @skillguard/cli scan SKILL.mdNo API key configured? CLI falls back to anonymous scanning (rate-limited).
Scan all skills in current repo
npx @skillguard/cli scanGenerate CI workflow (no secrets required)
npx @skillguard/cli workflowCommand help (man-like)
npx @skillguard/cli help scanSetup
npx @skillguard/cli initOr via auth command:
npx @skillguard/cli auth loginYou can also provide the key via env:
export SKILLGUARD_API_KEY="sgk_..."Usage
Scan one file
npx @skillguard/cli scan ./SKILL.mdBy default, scan verifies embedded signature metadata (if present) and can skip API rescans for unchanged files (Source: embedded (verified)).
Stable skill stream ID (recommended for badges)
npx @skillguard/cli scan ./SKILL.md --skill-id team/daily-ops-assistantRules:
- canonical format is
namespace/name(lowercase ASCII, digits, hyphens) - priority is
--skill-id> frontmatterskill_id> server-generated fallback - use
--force-skill-idto normalize legacy/non-canonical IDs during transition - collision rule: different raw IDs that normalize to the same canonical ID are treated as the same skill stream
Live scan narration (default)
npx @skillguard/cli scan ./SKILL.mdStreams stage-by-stage updates in real time using SSE (POST /api/v2/scan/stream) and prints each stage reply as a full line immediately.
Use --no-jokes to disable theatrical narration and keep plain stage progress output.
If streaming is unavailable, CLI automatically falls back to standard scan output.
ANSI colors can be disabled with NO_COLOR=1.
Edge-case behavior matrix
| Scenario | Behavior | Exit code |
|---|---|---|
| No API key configured | Anonymous scan mode + guidance to add API key | 0/1/2/3 by verdict |
| Invalid API key (401) | Sanitized auth error, suggests skillguard auth login | 5 |
| Anonymous rate limited (429) | Message suggests adding API key | 5 |
| Authenticated rate limited (429) | Message suggests waiting/upgrading | 5 |
| Server 502/503 | Retries once, then sanitized API error | 5 |
| Timeout / unreachable / DNS / TLS | Sanitized network timeout/error (no stack trace) | 5 |
| Empty dir or no SKILL.md | Deterministic no-op notice | 0 |
| Empty / binary / invalid UTF-8 SKILL.md | File skipped with warning; scan continues | 0 if nothing risky remains |
| Oversized file (>1MB) | Warns and scans first 1MB only | Verdict exit code |
| --format sarif + API failure | Emits valid SARIF (empty results) | 5 |
| --output unwritable | Sanitized write error | 4 |
| Redirected stdout / non-TTY | ANSI color and spinner UI disabled | Normal command exit code |
| Unexpected exception | Catch-all message, no stack trace | 5 |
Scan a directory
npx @skillguard/cli scan ./skills --fail-on warningJSON output for CI
npx @skillguard/cli scan ./skills --json > skillguard-report.jsonCI gate (recommended)
npx @skillguard/cli gate ./skillsCI gate without API key (allow partial scope)
npx @skillguard/cli gate ./skills --allow-partialCheck remaining limits
npx @skillguard/cli limitsAuth status and logout
npx @skillguard/cli auth status
npx @skillguard/cli auth logoutGenerate and push workflow in one command
npx @skillguard/cli workflow --auto-gh-pushWorst-score CI exit code (recommended)
npx @skillguard/cli scan
# exit: safe=0, warning=1, dangerous=2Verify signature
npx @skillguard/cli verify ./SKILL.mdOptions
scan
--json--fail-on <SAFE|WARNING|DANGEROUS|BLOCKED>(v2 threshold, default:WARNING)--fail-on-capability=<LOW|MEDIUM|HIGH|CRITICAL>--fail-on-intent=<LOW|MEDIUM|HIGH>--fail-on-policy-violation--fail-on-status=<SCANNED>--fail-on-hypothesis--timeout <ms>(default:30000)--base-url <url>(default:https://skillguard.ai)--api-key <key>--skill-id <namespace/name>--force-skill-id--dry-run--quiet--no-color--embed(default: enabled; write returnedsignedSkillContentinto existing frontmatter files)--no-embed(disable file write-back)--force(ignore embedded cache and always rescan via API)--no-jokes(disable theatrical narration; keep plain stage progress)--skip-node-modules(default: enabled)--scan-all(disable skip filters, scans everything recursively)
gate
- same options as
scan - defaults to CI gate behavior (
--fail-on warningin threshold mode) - defaults to
--no-embedbehavior unless--embedis explicitly set --allow-partial(do not fail-closed when API returns partial scope, e.g. anonymous Stage 1 only)
limits
--json--timeout <ms>(default:30000)--base-url <url>(default:https://skillguard.ai)--api-key <key>(optional)--no-color
auth
auth login [--api-key <key>] [--base-url <url>]auth status [--json] [--api-key <key>] [--base-url <url>]auth logout
workflow
--path <file>(default:.github/workflows/skillguard.yml)--scan-path <path>(default:.)--fail-on <safe|warning|dangerous>(default:warning)--print(print yaml, do not write file)--force(overwrite existing workflow file)--auto-gh-push(git add/commit/push after write)--every-push(trigger on every push, not just SKILL.md changes)
help / man
help [command]man [command]
verify
--json--timeout <ms>--base-url <url>
Exit codes
scan/gate(v2 verdicts):0SAFE1WARNING2DANGEROUS3BLOCKED (policy FAIL)
verify:0signature valid1invalid/tampered/expired signature
limits:0success
gate:0below threshold1threshold exceeded
workflow:0success
auth:0success
- all commands:
4usage/input/config error5network/API/runtime external failure
Embedded signature cache behavior
Cache hit requires all of:
- embedded
metadata.securityblock exists (legacy top-levelsecurityis also supported) - content hash matches
security.content_sha256 - Ed25519 signature verifies against SkillGuard JWKS
valid_untilis not expired
If any check fails, CLI falls back to API scan.
If gate cannot validate cache and API is unavailable, it fails closed with non-zero exit.
GitHub Actions example
name: SkillGuard Gate
on:
pull_request:
paths:
- '**/SKILL.md'
- '**/skill.md'
push:
paths:
- '**/SKILL.md'
- '**/skill.md'
jobs:
scan:
runs-on: ubuntu-latest
permissions:
actions: read
security-events: write
contents: read
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- run: |
fail_on=warning
gate_args=(. --fail-on "$fail_on" --format sarif --output skillguard-results.sarif)
if [ -z "${SKILLGUARD_API_KEY:-}" ]; then
fail_on=dangerous
gate_args=(. --fail-on "$fail_on" --format sarif --output skillguard-results.sarif)
gate_args+=(--allow-partial)
fi
npx @skillguard/cli gate "${gate_args[@]}"
env:
SKILLGUARD_API_KEY: ${{ secrets.SKILLGUARD_API_KEY }}
- uses: github/codeql-action/upload-sarif@v4
if: always()
with:
sarif_file: skillguard-results.sarifMore info: skillguard.ai
v2 Pipeline (2.0.0)
scan — Deep analysis with multi-stage LLM pipeline
npx @skillguard/cli scan ./SKILL.mdRuns the v2 pipeline (Stage 0 static → Stage 1 Flash-lite → Stage 2 Flash-lite → Stage 3 Flash-lite cross-check via Gemini) with policy evaluation and attestation. When the API returns badge metadata, the CLI also prints a ready-to-paste README snippet:
[](https://skillguard.ai/skill/owner/skill)v2 options
scan includes v2 controls:
| Flag | Description |
|------|-------------|
| --fail-on <SAFE\|WARNING\|DANGEROUS\|BLOCKED> | Base v2 risk threshold (default: WARNING) |
| --fail-on-capability=<LOW\|MEDIUM\|HIGH\|CRITICAL> | Fail if capability risk ≥ threshold |
| --fail-on-intent=<LOW\|MEDIUM\|HIGH> | Fail if intent suspicion ≥ threshold |
| --fail-on-policy-violation | Fail if any policy violation exists |
| --fail-on-status=<SCANNED> | Fail if analysis level matches (require VERIFIED) |
| --fail-on-hypothesis | Fail if any hypothesis flows detected |
| --no-jokes | Disable theatrical narration and print plain stage progress |
v2 exit codes
| Code | Meaning |
|------|---------|
| 0 | SAFE — below threshold |
| 1 | WARNING |
| 2 | DANGEROUS |
| 3 | BLOCKED (policy FAIL) |
| 4 | Usage/config error |
| 5 | Network/runtime failure |
v2 CI example
name: SkillGuard v2 Gate
on:
pull_request:
paths: ['**/SKILL.md', '**/skill.md']
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- run: npx @skillguard/cli scan . --fail-on WARNING --fail-on-policy-violationIf your CI intentionally runs without SKILLGUARD_API_KEY, use partial mode and a dangerous threshold:
npx @skillguard/cli gate . --fail-on dangerous --allow-partialv2 report output
The v2 CLI renders a box-drawing report including:
- Risk verdict, capability risk, intent suspicion
- Data flow evidence with
context_before/context_after - Policy evaluation status and violations
- "Evidence is for content hash
<sha256>" notice - Ed25519 attestation signature block
Changelog
2.1.2
- Added a TTY spinner for staged stream output while waiting between SSE replies
- Improved theatrical stream UX with clearer "pipeline is alive" feedback during network/model pauses
2.1.1
- Changed stream rendering from per-symbol typing to answer-by-answer line output
- Changed theatrical header text to
THE THREE MUSKETEERS AUDIT - Prevented duplicate full humor block when SSE stream succeeds (stream output remains stage-by-stage + final decree)
2.1.0
- Added realtime SSE streaming path via
/api/v2/scan/stream - Added resilient fallback to standard scan when streaming is unavailable
- Added Node tests for SSE humor stream parser/render flow
2.0.0
- v2 pipeline: 3-stage LLM analysis (Gemini Flash-lite across stages) with Zod validation and retry/fallback
scanv2 mode: v2-specific output and exit codes- 6 granular
--fail-on-*flags: capability, intent, policy-violation, status, hypothesis - Policy engine: 4 standard policies +
.skillguard.ymlconfig rules (max_intent_suspicion, deny_file_reads, require_verified_if_capability) - Attestation: RFC 8785 canonical 8-field payload, Ed25519 signing, JWKS verification
- Metering: VERIFIED quota per tier (FREE/TEAM/ENTERPRISE) with graceful degradation
- Punycode normalization: IDN homograph protection via
url.domainToASCII() - Evidence display: "Evidence is for content hash" in CLI output
