@skillguard/cli
v3.1.22
Published
Security scanner for AI agent skill files
Downloads
1,087
Maintainers
Readme
@skillguard/cli
Security scanner CLI for AI agent SKILL.md files.
Quick start
npx @skillguard/cli scan SKILL.mdNo saved CLI auth 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 scan
npx @skillguard/cli man workflowPrimary commands:
skillguard scan [path]— scan one file or recursively scan a directoryskillguard gate [path]— CI gate mode with deterministic pass/fail behaviorskillguard verify <path>— verify embedded SkillGuard signature via JWKSskillguard limits— show anonymous/owner/agent quota stateskillguard auth login|status|logout— manage CLI auth state (OAuth or API key)skillguard init— bootstrap local auth config and create default.skillguard.ymlskillguard workflow— generate GitHub Actions CI workflowskillguard update— check for and install latest CLI versionskillguard help [command]/skillguard man [command]— detailed command help
Setup
npx @skillguard/cli initOr via the shared backend OAuth flow:
npx @skillguard/cli auth loginNotes:
auth loginopens the browser, completes backend OAuth, and stores bearer/refresh tokens locally.auth login --api-key <key>stores an API key only.- neither
auth loginnorauth login --api-keywrites.skillguard.yml initremains the only command that bootstraps the project policy file
You 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)).
When OAuth login is configured and no API-key override is active, scan, gate, and verify use SkillGuard API endpoints over bearer auth.
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 ASCII IDs during transition - non-ASCII skill IDs are rejected to prevent NFKD canonicalization collisions
Live scan progress (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.
Plain stage progress is the default. Use --with-jokes to enable theatrical narration.
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 CLI auth configured | Anonymous scan mode + guidance to sign in or add an 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 signing in or adding an 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 limitslimits intentionally stays on the REST/API-key path in this phase. If only OAuth tokens are configured, the command falls back to anonymous limits and prints a hint.
Auth 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.mdVerification notes:
- customer-facing verification should rely on
signedSkillContent,signatureStatus,signatureType, andtamperEvident - do not rely on a raw top-level
securityobject being present in customer-facing v2 responses
Options
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:180000)--base-url <url>(default:https://skillguard.ai)--api-key <key>--yml <path>(override project policy YAML path)--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)--with-jokes(enable theatrical narration)--skip-node-modules(default: enabled)--scan-all(disable skip filters, scans everything recursively)
.skillguard.yml
The policy engine can read a repository-local .skillguard.yml file during v2 scans.
By default, the CLI reads <git-root>/.skillguard.yml; use --yml <path> to override.
skillguard init creates the default file when it is missing.
This file is not generated by skillguard workflow; the workflow command only writes .github/workflows/skillguard.yml.
Supported format:
version: "1.0"
enforcement_mode: blocking
treat_hypothesis_as: IGNORE
rules:
max_intent_suspicion: null
capabilities:
deny_file_reads: []
allowed_network_destinations: []
require_verified_if_capability: nullSupported values:
treat_hypothesis_as:IGNORE,WARN,FAILmax_intent_suspicion:LOW,MEDIUM,HIGHrequire_verified_if_capability:LOW,MEDIUM,HIGH,CRITICAL
Behavior notes:
IGNOREexcludes hypothesis-only flows from policy evaluation.WARNandFAILinclude hypothesis-only flows in policy evaluation.--fail-on-hypothesisis separate: it affects CLI exit behavior, not policy config.
Example:
version: "1.0"
treat_hypothesis_as: WARN
rules:
max_intent_suspicion: MEDIUM
capabilities:
deny_file_reads:
- ".env*"
- "**/secrets/**"
allowed_network_destinations:
- "api.openai.com"
- "*.githubusercontent.com"
require_verified_if_capability: HIGHgate
- same core scan options as
scan, except theatrical narration remains ascan-only toggle - supports
--yml <path>with the same behavior asscan - 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:180000)--base-url <url>(default:https://skillguard.ai)--api-key <key>(optional)--no-color
Behavior:
- explicit
--api-keyandSKILLGUARD_API_KEYforce API-key mode - stored config API key is used when present
- OAuth-only auth falls back to anonymous limits in this phase
auth
auth login [--api-key <key>] [--base-url <url>]auth status [--json] [--api-key <key>] [--base-url <url>]auth logout
Behavior:
- default
auth loginuses OAuth discovery + browser loopback callback auth login --api-keyis auth-only and does not create.skillguard.ymlinitis the bootstrap/policy command- if both stored OAuth and stored config API key exist,
scan/gate/verifyprefer OAuth;limitsprefers the stored config API key
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>
Behavior:
- with stored OAuth and no API-key override,
verifycalls SkillGuard API/JWKS endpoints - otherwise it falls back to local JWKS-based verification
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
security.issueris in trusted issuer allowlist (SKILLGUARD_TRUSTED_ISSUERS, default:https://skillguard.ai)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.
--force bypasses both embedded cache checks and server-side scan reuse.
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 |
| --with-jokes | Enable theatrical narration while preserving the existing theatrical output |
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
Maintainer release notes
The npm package is published from GitHub Actions via Trusted Publishing.
One-time npm setup:
- In npm package settings for
@skillguard/cli, add a GitHub Actions trusted publisher. - Use repository
Lionberg/SkillGuard. - Use workflow filename
publish-cli.yml.
Release flow:
git tag cli-v3.1.14
git push origin cli-v3.1.14Or run the Publish CLI workflow manually from GitHub Actions.
Safety checks in the workflow:
- installs from the repo root with
npm ci - builds
@skillguard/shared,@skillguard/skill-security-core, and@skillguard/cli - runs
npm test --workspace @skillguard/cli - verifies tag version matches
packages/cli/package.json - publishes only from
packages/cli
