Skip to content

CLI: lint

Terminal window
vally lint [path] [options]

vally lint is the home for fast static checks on the assets in your Vally project — no agent execution required. It validates eval specs (eval.yaml) for configuration errors and checks any SKILL.md files in the given path against the spec graders. All checks complete in seconds.

ArgumentRequiredDefaultDescription
pathNoCurrent directoryRoot directory to scan for SKILL.md files
FlagTypeDefaultDescription
--eval-spec <path>stringPath to an eval spec file to validate
--known-domains <path>stringPath to a known-domains file for URL reference scanning
--allowed-external-deps <path>stringPath to an allowlist file for structural dependency scanning
--grader-plugin <specifier>stringGrader plugin to load (npm package name or local path). Prevents unknown-grader-type errors for plugin graders.
--strictbooleanfalseTreat warnings as errors (exit 1 on any diagnostic, including dependency warnings)
--verbosebooleanfalseShow detailed grader evidence for each check
CodeMeaning
0All checks passed (warnings may be present unless --strict)
1One or more skills failed, a reference error was found, an eval spec error was found, or an error occurred

When --eval-spec is provided, the following checks run against the eval YAML:

CodeWhat it catches
unknown-grader-typeGrader type not in registry. Includes fuzzy “did you mean?” suggestions.
invalid-grader-configUnknown config keys, missing required fields, wrong types.
invalid-scoring-weight-keyScoring weights reference a grader type not used in any stimulus.
invalid-constraint-valueNegative limits, overlapping expect/reject lists.
environment-file-not-foundEnvironment files[].src path doesn’t exist on disk.
duplicate-stimulus-nameTwo stimuli share the same name.
CodeWhat it surfaces
duplicate-grader-type-in-stimulusSame grader type used twice in one stimulus (shared scoring weight key).
rubric-without-llm-judgerubric defined but no LLM judge grader configured.
regression-without-baselinetype: regression set but no baseline configuration.
graders-without-executionGraders defined but runs: 0 or no executor.
scoring-defaults-appliedNo scoring block defined (using default: equal weights, threshold 0).

When a path is provided, vally lint discovers all SKILL.md files under it and runs three built-in graders:

GraderWhat it checks
spec-complianceName format (kebab-case, ≤ 64 chars), description presence and length (≤ 1024 chars), valid frontmatter
valid-refsEvery file path referenced in the SKILL.md exists on disk
orphan-filesNo files in the references/ directory are unreachable from the SKILL.md

When --known-domains is provided, all SKILL.md, *.agent.md, and references/*.md files within discovered skill directories are scanned for URL reference issues:

CodeWhat it catches
EXTERNAL-DOMAINURL domain not in the known-domains allowlist
HTTP-NOT-HTTPSInsecure http:// URL (localhost and schemas.microsoft.com are exempt)
PIPE-TO-SHELLcurl or wget piped directly to a shell interpreter
SCRIPT-NO-SRIExternal <script> tag without integrity (SRI) attribute

One domain per line. Lines starting with # are comments, blank lines are ignored.

  • Bare domains match the host and any subdomain: microsoft.com matches learn.microsoft.com
  • Path-scoped entries require the URL prefix to match: github.com/dotnet/runtime matches github.com/dotnet/runtime/issues but not github.com/dotnet/sdk
known-domains.txt
# Core domains
microsoft.com
github.com
# Path-scoped
github.com/dotnet/runtime

When --allowed-external-deps is provided, each discovered skill is checked for structural external dependencies. These findings are advisory warnings — they do not fail the lint unless --strict is used.

CodeWhat it catches
SCRIPT-FILEScript file (.ps1, .sh, .py, .bat, .cmd, .bash) in the skill’s scripts/ directory
INVOKES-SCRIPTSkill description contains an INVOKES <script> pattern
NON-BUILTIN-TOOL-REF#tool:xxx reference to a non-built-in tool in the skill content

One entry per line. Lines starting with # are comments, blank lines are ignored. Keys are case-insensitive and use the format type:skill-name:detail:

allowed-external-deps.txt
# Allow the setup script in my-skill
script:my-skill:scripts/setup.ps1
# Allow the INVOKES pattern in nullable-ref skill
invokes:nullable-ref
# Allow a specific tool reference
tool-ref:my-skill:#tool:web/fetch

Each warning message includes the exact (allow: ...) key to add to the allowlist file.

Terminal window
# Validate eval spec only
vally lint --eval-spec eval.yaml
# Validate eval spec AND lint SKILL.md files
vally lint ./skills --eval-spec eval.yaml
Terminal window
# Lint current directory
vally lint
# Lint a specific skill
vally lint ./skills/my-skill
# Lint with detailed output
vally lint ./skills --verbose
Terminal window
# Fail on any warnings too
vally lint . --eval-spec eval.yaml --strict
Terminal window
# Check URLs against a known-domains list
vally lint ./skills --known-domains known-domains.txt
# Combine with eval validation
vally lint ./skills --known-domains known-domains.txt --eval-spec eval.yaml
Terminal window
# Check for external dependencies
vally lint ./skills --allowed-external-deps allowed-external-deps.txt
# Fail on dependency warnings in CI
vally lint ./skills --allowed-external-deps allowed-external-deps.txt --strict

On success:

✔ eval.yaml is valid

With issues:

Eval: eval.yaml
✘ error [unknown-grader-type] Unknown grader type "output-contain"
at stimuli[0].graders[0].type
Did you mean "output-contains"?
⚠ warning [scoring-defaults-applied] No scoring configuration; using equal weights, threshold 0
at scoring
1 error(s), 1 warning(s)
Found 2 skill(s): auth-helper, code-reviewer
✔ auth-helper
✔ spec-compliance (4/4 checks passed)
✔ valid-refs (3 references, 0 broken)
✔ orphan-files (0 orphaned files)
✘ code-reviewer
✘ spec-compliance (3/4 checks passed)
✗ name-format: Name must be kebab-case
✔ valid-refs
✔ orphan-files
1/2 skills passed