The validator for skills in the Gitlawb Skill Hub
registry. It is the single source of truth for what makes a skill
valid — the registry's CI, the openclaude CLI, and any third-party
tooling all run the same rules through this package.
npm install @gitlawb/skill-validator
import { validateSkill } from '@gitlawb/skill-validator';
const content = `---
name: my-skill
description: Example skill used in the README.
trust: community
version: 0.1.0
license: MIT
---
# My Skill
A body long enough to clear the 200-character minimum so this example
parses cleanly without tripping the body-length rule.
## Use this skill when
- triggers.
## Procedure
1. step.
`;
const result = validateSkill(content);
if (!result.ok) {
for (const error of result.errors) {
const where = error.line !== undefined ? `:${error.line}` : '';
console.error(`[${error.code}]${where} ${error.message}`);
}
}import { validateSkillFolder } from '@gitlawb/skill-validator';
const result = await validateSkillFolder('skills/pr-review');
console.log(result.ok ? 'OK' : 'errors');
for (const w of result.warnings) console.warn(w.message);validateSkillFolder adds folder-level rules on top of validateSkill:
file allowlist (SKILL.md, README.md, plus reserved
.skill-meta.json), no symlinks, no other hidden files, 1 MB total
size cap.
interface ValidationResult {
ok: boolean;
errors: ValidationError[];
warnings: ValidationWarning[];
parsed?: ParsedSkill;
}
interface ValidationError {
code: string; // e.g. "frontmatter.invalid_license"
message: string;
field?: string; // for frontmatter findings
line?: number; // for body/scanner findings
snippet?: string; // <= 100 chars
}
// ValidationWarning has the same shape as ValidationError.Every finding has a stable code string so callers can pattern-match
on the rule without parsing the human-readable message. Codes are
grouped by source: frontmatter.*, body.*, scanner.*, files.*.
- Frontmatter — required fields (
name,description,trust,version,license), optional fields (title,category,tags,author,compatibility), and an unknown-field warning. Semver via an inline regex; license against the SPDX list. - Body — minimum 200 characters, maximum 100 KB file size,
required section headers (
## Use this skill when,## Procedure), warnings for missing recommended headers. - Security scanner — flags
curl/wgetagainst external URLs, secret-style identifiers paired with URLs, base64+eval/exec,rm -rfagainst absolute paths, and<script>tags — all outside fenced code blocks. Warns on URLs pointing at raw IPs. - Folder policy — only
SKILL.mdandREADME.mdat the top level (plus the reserved.skill-meta.json), no symlinks, no other hidden files, 1 MB total size cap.
See DECISIONS.md
in the registry repo for the schema this validator enforces and the
policy behind it.
- Fixed: SPDX license ID list is now inlined as a TypeScript constant.
Removes the runtime JSON import that crashed under Node 22 / 24 strict
ESM with
[ERR_IMPORT_ATTRIBUTE_MISSING]. The list is generated bypackages/validator/scripts/gen-spdx-ids.tsand committed atsrc/spdx-ids.ts; regenerate withbun run scripts/gen-spdx-ids.ts. - Added:
engines: { "node": ">=20" }topackage.json. - Internal: consumer integration test
(
scripts/__tests__/consumer-integration.test.ts) installs the packed tarball into a fresh ESM project and runsvalidateSkillvia plainnode— the exact path real consumers walk.
- Initial release.
MIT