skill-check 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +360 -0
  3. package/bin/skill-check.js +11 -0
  4. package/dist/cli/main.d.ts +5 -0
  5. package/dist/cli/main.js +724 -0
  6. package/dist/core/agent-scan.d.ts +19 -0
  7. package/dist/core/agent-scan.js +88 -0
  8. package/dist/core/allowlist.d.ts +1 -0
  9. package/dist/core/allowlist.js +8 -0
  10. package/dist/core/analyze.d.ts +6 -0
  11. package/dist/core/analyze.js +72 -0
  12. package/dist/core/artifact.d.ts +2 -0
  13. package/dist/core/artifact.js +33 -0
  14. package/dist/core/baseline.d.ts +8 -0
  15. package/dist/core/baseline.js +17 -0
  16. package/dist/core/config.d.ts +2 -0
  17. package/dist/core/config.js +215 -0
  18. package/dist/core/defaults.d.ts +6 -0
  19. package/dist/core/defaults.js +34 -0
  20. package/dist/core/discovery.d.ts +2 -0
  21. package/dist/core/discovery.js +46 -0
  22. package/dist/core/duplicates.d.ts +2 -0
  23. package/dist/core/duplicates.js +60 -0
  24. package/dist/core/errors.d.ts +4 -0
  25. package/dist/core/errors.js +8 -0
  26. package/dist/core/fix.d.ts +11 -0
  27. package/dist/core/fix.js +172 -0
  28. package/dist/core/formatters.d.ts +4 -0
  29. package/dist/core/formatters.js +182 -0
  30. package/dist/core/frontmatter.d.ts +7 -0
  31. package/dist/core/frontmatter.js +39 -0
  32. package/dist/core/github-formatter.d.ts +2 -0
  33. package/dist/core/github-formatter.js +10 -0
  34. package/dist/core/html-report.d.ts +3 -0
  35. package/dist/core/html-report.js +320 -0
  36. package/dist/core/interactive-fix.d.ts +9 -0
  37. package/dist/core/interactive-fix.js +22 -0
  38. package/dist/core/links.d.ts +17 -0
  39. package/dist/core/links.js +94 -0
  40. package/dist/core/open-browser.d.ts +6 -0
  41. package/dist/core/open-browser.js +20 -0
  42. package/dist/core/plugins.d.ts +2 -0
  43. package/dist/core/plugins.js +41 -0
  44. package/dist/core/quality-score.d.ts +14 -0
  45. package/dist/core/quality-score.js +55 -0
  46. package/dist/core/report.d.ts +2 -0
  47. package/dist/core/report.js +26 -0
  48. package/dist/core/rule-engine.d.ts +2 -0
  49. package/dist/core/rule-engine.js +52 -0
  50. package/dist/core/sarif.d.ts +2 -0
  51. package/dist/core/sarif.js +48 -0
  52. package/dist/index.d.ts +11 -0
  53. package/dist/index.js +8 -0
  54. package/dist/rules/core/body.d.ts +2 -0
  55. package/dist/rules/core/body.js +39 -0
  56. package/dist/rules/core/description.d.ts +2 -0
  57. package/dist/rules/core/description.js +66 -0
  58. package/dist/rules/core/file.d.ts +2 -0
  59. package/dist/rules/core/file.js +26 -0
  60. package/dist/rules/core/frontmatter.d.ts +2 -0
  61. package/dist/rules/core/frontmatter.js +124 -0
  62. package/dist/rules/core/index.d.ts +2 -0
  63. package/dist/rules/core/index.js +12 -0
  64. package/dist/rules/core/links.d.ts +2 -0
  65. package/dist/rules/core/links.js +54 -0
  66. package/dist/types.d.ts +92 -0
  67. package/dist/types.js +1 -0
  68. package/package.json +82 -0
  69. package/schemas/config.schema.json +53 -0
  70. package/skills/skill-check/SKILL.md +76 -0
@@ -0,0 +1,52 @@
1
+ import { isAllowlisted } from './allowlist.js';
2
+ function resolveRuleLevel(config, ruleId, fallback) {
3
+ const override = config.rules[ruleId];
4
+ if (override)
5
+ return override;
6
+ return fallback;
7
+ }
8
+ export async function runRuleEngine(skills, rules, config) {
9
+ const diagnostics = [];
10
+ const context = {
11
+ config,
12
+ resolveRuleLevel: (ruleId, fallback) => resolveRuleLevel(config, ruleId, fallback),
13
+ };
14
+ for (const skill of skills) {
15
+ for (const rule of rules) {
16
+ const level = resolveRuleLevel(config, rule.id, rule.defaultSeverity);
17
+ if (level === 'off')
18
+ continue;
19
+ const findings = await rule.evaluate(skill, context);
20
+ if (!findings || findings.length === 0)
21
+ continue;
22
+ for (const finding of findings) {
23
+ if (isAllowlisted(skill.id, config.allowlist)) {
24
+ continue;
25
+ }
26
+ let severity = finding.severity ?? (level === 'error' ? 'error' : 'warn');
27
+ if (config.strictMode && severity === 'warn') {
28
+ severity = 'error';
29
+ }
30
+ diagnostics.push({
31
+ ruleId: rule.id,
32
+ severity,
33
+ message: finding.message,
34
+ file: skill.relativePath,
35
+ line: finding.line ?? 1,
36
+ column: finding.column ?? 1,
37
+ suggestion: finding.suggestion,
38
+ });
39
+ }
40
+ }
41
+ }
42
+ diagnostics.sort((a, b) => {
43
+ if (a.file !== b.file)
44
+ return a.file.localeCompare(b.file);
45
+ if (a.line !== b.line)
46
+ return a.line - b.line;
47
+ if (a.column !== b.column)
48
+ return a.column - b.column;
49
+ return a.ruleId.localeCompare(b.ruleId);
50
+ });
51
+ return diagnostics;
52
+ }
@@ -0,0 +1,2 @@
1
+ import type { AnalysisResult } from '../types.js';
2
+ export declare function toSarif(result: AnalysisResult): Record<string, unknown>;
@@ -0,0 +1,48 @@
1
+ export function toSarif(result) {
2
+ const rulesMap = new Map();
3
+ for (const diagnostic of result.diagnostics) {
4
+ if (!rulesMap.has(diagnostic.ruleId)) {
5
+ rulesMap.set(diagnostic.ruleId, {
6
+ id: diagnostic.ruleId,
7
+ shortDescription: {
8
+ text: diagnostic.ruleId,
9
+ },
10
+ });
11
+ }
12
+ }
13
+ return {
14
+ version: '2.1.0',
15
+ $schema: 'https://json.schemastore.org/sarif-2.1.0.json',
16
+ runs: [
17
+ {
18
+ tool: {
19
+ driver: {
20
+ name: 'skill-check',
21
+ informationUri: 'https://github.com/thedaviddias/skill-check',
22
+ rules: Array.from(rulesMap.values()),
23
+ },
24
+ },
25
+ results: result.diagnostics.map((diagnostic) => ({
26
+ ruleId: diagnostic.ruleId,
27
+ level: diagnostic.severity === 'error' ? 'error' : 'warning',
28
+ message: {
29
+ text: diagnostic.message,
30
+ },
31
+ locations: [
32
+ {
33
+ physicalLocation: {
34
+ artifactLocation: {
35
+ uri: diagnostic.file,
36
+ },
37
+ region: {
38
+ startLine: diagnostic.line,
39
+ startColumn: diagnostic.column,
40
+ },
41
+ },
42
+ },
43
+ ],
44
+ })),
45
+ },
46
+ ],
47
+ };
48
+ }
@@ -0,0 +1,11 @@
1
+ export { analyze, resolveExitCode } from './core/analyze.js';
2
+ export type { BaselineDiff } from './core/baseline.js';
3
+ export { diffBaseline, loadBaseline } from './core/baseline.js';
4
+ export { resolveConfig } from './core/config.js';
5
+ export { detectDuplicates } from './core/duplicates.js';
6
+ export { toGitHubAnnotations } from './core/github-formatter.js';
7
+ export { renderHtml } from './core/html-report.js';
8
+ export type { SkillScore } from './core/quality-score.js';
9
+ export { computeSkillScores } from './core/quality-score.js';
10
+ export { coreRules } from './rules/core/index.js';
11
+ export type { AnalysisResult, CliOptions, Diagnostic, PluginModule, ResolvedConfig, RuleDefinition, RuleFinding, Severity, SkillArtifact, } from './types.js';
package/dist/index.js ADDED
@@ -0,0 +1,8 @@
1
+ export { analyze, resolveExitCode } from './core/analyze.js';
2
+ export { diffBaseline, loadBaseline } from './core/baseline.js';
3
+ export { resolveConfig } from './core/config.js';
4
+ export { detectDuplicates } from './core/duplicates.js';
5
+ export { toGitHubAnnotations } from './core/github-formatter.js';
6
+ export { renderHtml } from './core/html-report.js';
7
+ export { computeSkillScores } from './core/quality-score.js';
8
+ export { coreRules } from './rules/core/index.js';
@@ -0,0 +1,2 @@
1
+ import type { RuleDefinition } from '../../types.js';
2
+ export declare const bodyRules: RuleDefinition[];
@@ -0,0 +1,39 @@
1
+ function estimateTokens(text) {
2
+ return text.split(/\s+/).filter(Boolean).length;
3
+ }
4
+ export const bodyRules = [
5
+ {
6
+ id: 'body.max_lines',
7
+ description: 'Body should stay within configured line limit.',
8
+ defaultSeverity: 'error',
9
+ evaluate(skill, context) {
10
+ const lines = skill.body.split(/\r?\n/).length;
11
+ const max = context.config.limits.maxBodyLines;
12
+ if (lines <= max)
13
+ return [];
14
+ return [
15
+ {
16
+ message: `body lines ${lines} exceeds max ${max}`,
17
+ suggestion: `Reduce body to ${max} lines or fewer.`,
18
+ },
19
+ ];
20
+ },
21
+ },
22
+ {
23
+ id: 'body.max_tokens',
24
+ description: 'Body should stay within configured token limit.',
25
+ defaultSeverity: 'warn',
26
+ evaluate(skill, context) {
27
+ const tokens = estimateTokens(skill.body);
28
+ const max = context.config.limits.maxBodyTokens;
29
+ if (tokens <= max)
30
+ return [];
31
+ return [
32
+ {
33
+ message: `body token estimate ${tokens} exceeds max ${max}`,
34
+ suggestion: `Reduce body to ~${max} tokens or fewer. Token count is a whitespace-split approximation.`,
35
+ },
36
+ ];
37
+ },
38
+ },
39
+ ];
@@ -0,0 +1,2 @@
1
+ import type { RuleDefinition } from '../../types.js';
2
+ export declare const descriptionRules: RuleDefinition[];
@@ -0,0 +1,66 @@
1
+ function getDescription(skill) {
2
+ if (!skill.frontmatter)
3
+ return '';
4
+ const value = skill.frontmatter.description;
5
+ if (typeof value !== 'string')
6
+ return '';
7
+ return value.trim();
8
+ }
9
+ export const descriptionRules = [
10
+ {
11
+ id: 'description.max_length',
12
+ description: 'Description must be within configured max length.',
13
+ defaultSeverity: 'error',
14
+ evaluate(skill, context) {
15
+ const description = getDescription(skill);
16
+ if (!description)
17
+ return [];
18
+ const max = context.config.limits.maxDescriptionChars;
19
+ if (description.length <= max)
20
+ return [];
21
+ return [
22
+ {
23
+ message: `description length ${description.length} exceeds max ${max}`,
24
+ suggestion: `Shorten description to ${max} characters or fewer.`,
25
+ },
26
+ ];
27
+ },
28
+ },
29
+ {
30
+ id: 'description.use_when_phrase',
31
+ description: 'Description should include "Use when" phrasing.',
32
+ defaultSeverity: 'warn',
33
+ evaluate(skill) {
34
+ const description = getDescription(skill);
35
+ if (!description)
36
+ return [];
37
+ if (/\buse\s+when\b/i.test(description))
38
+ return [];
39
+ return [
40
+ {
41
+ message: 'description should contain "Use when" phrasing',
42
+ suggestion: 'Start description with "Use when" to help agents match this skill to user intent.',
43
+ },
44
+ ];
45
+ },
46
+ },
47
+ {
48
+ id: 'description.min_recommended_length',
49
+ description: 'Description should meet recommended minimum length.',
50
+ defaultSeverity: 'warn',
51
+ evaluate(skill, context) {
52
+ const description = getDescription(skill);
53
+ if (!description)
54
+ return [];
55
+ const min = context.config.limits.minDescriptionChars;
56
+ if (description.length >= min)
57
+ return [];
58
+ return [
59
+ {
60
+ message: `description is short (${description.length} chars), recommended min is ${min}`,
61
+ suggestion: `Add more detail about when and why an agent should use this skill.`,
62
+ },
63
+ ];
64
+ },
65
+ },
66
+ ];
@@ -0,0 +1,2 @@
1
+ import type { RuleDefinition } from '../../types.js';
2
+ export declare const fileRules: RuleDefinition[];
@@ -0,0 +1,26 @@
1
+ export const fileRules = [
2
+ {
3
+ id: 'file.trailing_newline_single',
4
+ description: 'File should end with exactly one trailing newline.',
5
+ defaultSeverity: 'warn',
6
+ evaluate(skill) {
7
+ if (!skill.content.endsWith('\n')) {
8
+ return [
9
+ {
10
+ message: 'file should end with a newline',
11
+ suggestion: 'Add a single newline at the end of the file.',
12
+ },
13
+ ];
14
+ }
15
+ if (skill.content.endsWith('\n\n')) {
16
+ return [
17
+ {
18
+ message: 'file has multiple trailing newlines; use a single newline',
19
+ suggestion: 'Remove extra blank lines at the end of the file.',
20
+ },
21
+ ];
22
+ }
23
+ return [];
24
+ },
25
+ },
26
+ ];
@@ -0,0 +1,2 @@
1
+ import type { RuleDefinition } from '../../types.js';
2
+ export declare const frontmatterRules: RuleDefinition[];
@@ -0,0 +1,124 @@
1
+ function normalizeName(name) {
2
+ if (typeof name !== 'string')
3
+ return '';
4
+ return name.replace(/^['"]|['"]$/g, '').trim();
5
+ }
6
+ export const frontmatterRules = [
7
+ {
8
+ id: 'frontmatter.required',
9
+ description: 'SKILL.md must have valid YAML frontmatter.',
10
+ defaultSeverity: 'error',
11
+ evaluate(skill) {
12
+ if (skill.frontmatter)
13
+ return [];
14
+ return [
15
+ {
16
+ message: skill.parseError ??
17
+ 'missing or invalid YAML frontmatter (--- ... ---)',
18
+ suggestion: 'Add YAML frontmatter at the top: ---\\nname: my-skill\\ndescription: Use when ...\\n---',
19
+ line: 1,
20
+ column: 1,
21
+ },
22
+ ];
23
+ },
24
+ },
25
+ {
26
+ id: 'frontmatter.name_required',
27
+ description: 'Frontmatter must include name.',
28
+ defaultSeverity: 'error',
29
+ evaluate(skill) {
30
+ if (!skill.frontmatter)
31
+ return [];
32
+ if (skill.frontmatter.name)
33
+ return [];
34
+ return [
35
+ {
36
+ message: 'missing frontmatter "name"',
37
+ suggestion: `Add "name: ${skill.slug}" to frontmatter.`,
38
+ line: 1,
39
+ column: 1,
40
+ },
41
+ ];
42
+ },
43
+ },
44
+ {
45
+ id: 'frontmatter.description_required',
46
+ description: 'Frontmatter must include description.',
47
+ defaultSeverity: 'error',
48
+ evaluate(skill) {
49
+ if (!skill.frontmatter)
50
+ return [];
51
+ if (skill.frontmatter.description)
52
+ return [];
53
+ return [
54
+ {
55
+ message: 'missing frontmatter "description"',
56
+ suggestion: 'Add a "description:" field. Start with "Use when" to explain triggers.',
57
+ line: 1,
58
+ column: 1,
59
+ },
60
+ ];
61
+ },
62
+ },
63
+ {
64
+ id: 'frontmatter.name_matches_directory',
65
+ description: 'Frontmatter name must match skill directory slug.',
66
+ defaultSeverity: 'error',
67
+ evaluate(skill) {
68
+ if (!skill.frontmatter)
69
+ return [];
70
+ const name = normalizeName(skill.frontmatter.name);
71
+ if (!name)
72
+ return [];
73
+ if (name === skill.slug)
74
+ return [];
75
+ return [
76
+ {
77
+ message: `name "${name}" does not match directory "${skill.slug}"`,
78
+ suggestion: `Rename to "name: ${skill.slug}" or rename the directory to "${name}".`,
79
+ },
80
+ ];
81
+ },
82
+ },
83
+ {
84
+ id: 'frontmatter.name_slug_format',
85
+ description: 'Frontmatter name must match slug spec.',
86
+ defaultSeverity: 'error',
87
+ evaluate(skill) {
88
+ if (!skill.frontmatter)
89
+ return [];
90
+ const name = normalizeName(skill.frontmatter.name);
91
+ if (!name)
92
+ return [];
93
+ if (/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(name))
94
+ return [];
95
+ return [
96
+ {
97
+ message: 'name must use lowercase letters, numbers, and hyphens only',
98
+ suggestion: `Use "name: ${skill.slug}" (derived from directory name).`,
99
+ },
100
+ ];
101
+ },
102
+ },
103
+ {
104
+ id: 'frontmatter.field_order',
105
+ description: 'Frontmatter should list name before description.',
106
+ defaultSeverity: 'error',
107
+ evaluate(skill) {
108
+ if (!skill.frontmatterRaw)
109
+ return [];
110
+ const nameIndex = skill.frontmatterRaw.indexOf('name:');
111
+ const descriptionIndex = skill.frontmatterRaw.indexOf('description:');
112
+ if (nameIndex === -1 || descriptionIndex === -1)
113
+ return [];
114
+ if (nameIndex < descriptionIndex)
115
+ return [];
116
+ return [
117
+ {
118
+ message: 'frontmatter field order should be: name, description',
119
+ suggestion: 'Reorder so "name:" comes before "description:" in frontmatter.',
120
+ },
121
+ ];
122
+ },
123
+ },
124
+ ];
@@ -0,0 +1,2 @@
1
+ import type { RuleDefinition } from '../../types.js';
2
+ export declare const coreRules: RuleDefinition[];
@@ -0,0 +1,12 @@
1
+ import { bodyRules } from './body.js';
2
+ import { descriptionRules } from './description.js';
3
+ import { fileRules } from './file.js';
4
+ import { frontmatterRules } from './frontmatter.js';
5
+ import { linkRules } from './links.js';
6
+ export const coreRules = [
7
+ ...frontmatterRules,
8
+ ...descriptionRules,
9
+ ...bodyRules,
10
+ ...fileRules,
11
+ ...linkRules,
12
+ ];
@@ -0,0 +1,2 @@
1
+ import type { RuleDefinition } from '../../types.js';
2
+ export declare const linkRules: RuleDefinition[];
@@ -0,0 +1,54 @@
1
+ import fs from 'node:fs';
2
+ import { extractMarkdownLinks, isLocalResolvableTarget, resolveLocalTarget, } from '../../core/links.js';
3
+ export const linkRules = [
4
+ {
5
+ id: 'links.local_markdown_resolves',
6
+ description: 'Local markdown links should resolve.',
7
+ defaultSeverity: 'warn',
8
+ evaluate(skill) {
9
+ const links = extractMarkdownLinks(skill.content);
10
+ const findings = [];
11
+ for (const link of links) {
12
+ if (!isLocalResolvableTarget(link.normalizedTarget))
13
+ continue;
14
+ if (link.normalizedTarget.startsWith('references/'))
15
+ continue;
16
+ const target = resolveLocalTarget(skill.filePath, link.normalizedTarget);
17
+ if (!fs.existsSync(target)) {
18
+ findings.push({
19
+ message: `broken local link: ${link.rawTarget}`,
20
+ suggestion: `Create the file at ${link.normalizedTarget} or fix the link path.`,
21
+ line: link.line,
22
+ column: link.column,
23
+ });
24
+ }
25
+ }
26
+ return findings;
27
+ },
28
+ },
29
+ {
30
+ id: 'links.references_resolve',
31
+ description: 'references/* links should resolve within skill directory.',
32
+ defaultSeverity: 'warn',
33
+ evaluate(skill) {
34
+ const links = extractMarkdownLinks(skill.content);
35
+ const findings = [];
36
+ for (const link of links) {
37
+ if (!isLocalResolvableTarget(link.normalizedTarget))
38
+ continue;
39
+ if (!link.normalizedTarget.startsWith('references/'))
40
+ continue;
41
+ const target = resolveLocalTarget(skill.filePath, link.normalizedTarget);
42
+ if (!fs.existsSync(target)) {
43
+ findings.push({
44
+ message: `broken references link: ${link.rawTarget}`,
45
+ suggestion: `Create ${link.normalizedTarget} in the skill directory or remove the link.`,
46
+ line: link.line,
47
+ column: link.column,
48
+ });
49
+ }
50
+ }
51
+ return findings;
52
+ },
53
+ },
54
+ ];
@@ -0,0 +1,92 @@
1
+ export type Severity = 'error' | 'warn';
2
+ export type RuleLevel = Severity | 'off';
3
+ export type OutputFormat = 'text' | 'json' | 'sarif' | 'html' | 'github';
4
+ export interface Diagnostic {
5
+ ruleId: string;
6
+ severity: Severity;
7
+ message: string;
8
+ file: string;
9
+ line: number;
10
+ column: number;
11
+ suggestion?: string;
12
+ }
13
+ export interface RuleFinding {
14
+ message: string;
15
+ line?: number;
16
+ column?: number;
17
+ suggestion?: string;
18
+ severity?: Severity;
19
+ }
20
+ export interface LimitsConfig {
21
+ maxDescriptionChars: number;
22
+ maxBodyLines: number;
23
+ minDescriptionChars: number;
24
+ maxBodyTokens: number;
25
+ }
26
+ export interface OutputConfig {
27
+ format: OutputFormat;
28
+ reportPath?: string;
29
+ }
30
+ export interface ResolvedConfig {
31
+ cwd: string;
32
+ configPath?: string;
33
+ roots: string[];
34
+ rootsAbs: string[];
35
+ include: string[];
36
+ exclude: string[];
37
+ limits: LimitsConfig;
38
+ rules: Record<string, RuleLevel>;
39
+ allowlist: string[];
40
+ plugins: string[];
41
+ output: OutputConfig;
42
+ failOnWarning: boolean;
43
+ strictMode: boolean;
44
+ lenientMode: boolean;
45
+ }
46
+ export interface SkillArtifact {
47
+ id: string;
48
+ category: string;
49
+ slug: string;
50
+ filePath: string;
51
+ relativePath: string;
52
+ content: string;
53
+ body: string;
54
+ frontmatter: Record<string, unknown> | null;
55
+ frontmatterRaw: string | null;
56
+ parseError?: string;
57
+ }
58
+ export interface RuleContext {
59
+ config: ResolvedConfig;
60
+ resolveRuleLevel: (ruleId: string, fallback: RuleLevel) => RuleLevel;
61
+ }
62
+ export interface RuleDefinition {
63
+ id: string;
64
+ description: string;
65
+ defaultSeverity: Severity;
66
+ evaluate: (skill: SkillArtifact, context: RuleContext) => RuleFinding[] | Promise<RuleFinding[]>;
67
+ }
68
+ export interface PluginModule {
69
+ rules: RuleDefinition[];
70
+ }
71
+ export interface Summary {
72
+ skillCount: number;
73
+ errorCount: number;
74
+ warningCount: number;
75
+ }
76
+ export interface AnalysisResult {
77
+ config: ResolvedConfig;
78
+ skills: SkillArtifact[];
79
+ diagnostics: Diagnostic[];
80
+ summary: Summary;
81
+ }
82
+ export interface CliOptions {
83
+ configPath?: string;
84
+ format?: OutputFormat;
85
+ strict?: boolean;
86
+ lenient?: boolean;
87
+ maxBodyLines?: number;
88
+ maxDescriptionChars?: number;
89
+ include?: string[];
90
+ exclude?: string[];
91
+ failOnWarning?: boolean;
92
+ }
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
package/package.json ADDED
@@ -0,0 +1,82 @@
1
+ {
2
+ "name": "skill-check",
3
+ "version": "0.1.0",
4
+ "description": "Linter for agent skill files",
5
+ "type": "module",
6
+ "private": false,
7
+ "bin": {
8
+ "skill-check": "bin/skill-check.js"
9
+ },
10
+ "files": [
11
+ "bin",
12
+ "dist",
13
+ "schemas",
14
+ "skills",
15
+ "README.md",
16
+ "LICENSE"
17
+ ],
18
+ "engines": {
19
+ "node": ">=20"
20
+ },
21
+ "scripts": {
22
+ "clean": "rm -rf dist coverage",
23
+ "build": "tsc -p tsconfig.build.json",
24
+ "dev": "tsx src/cli/main.ts",
25
+ "check": "tsx src/cli/main.ts check .",
26
+ "check:fix": "tsx src/cli/main.ts check . --fix --no-security-scan",
27
+ "check:security": "tsx src/cli/main.ts check . --security-scan --allow-installs",
28
+ "security-scan": "tsx src/cli/main.ts security-scan .",
29
+ "demo:readme": "bash scripts/readme-demo.sh",
30
+ "release": "semantic-release",
31
+ "release:dry-run": "semantic-release --dry-run",
32
+ "smoke:cli": "bash scripts/smoke-cli.sh",
33
+ "report": "tsx src/cli/main.ts report .",
34
+ "lint": "biome check --no-errors-on-unmatched .",
35
+ "typecheck": "tsc -p tsconfig.json --noEmit",
36
+ "test": "vitest run",
37
+ "test:watch": "vitest",
38
+ "prepack": "pnpm run clean && pnpm run build",
39
+ "format": "biome format --write .",
40
+ "prepare": "lefthook install"
41
+ },
42
+ "keywords": [
43
+ "skills",
44
+ "agent-skills",
45
+ "lint",
46
+ "validator",
47
+ "cli",
48
+ "sarif"
49
+ ],
50
+ "repository": {
51
+ "type": "git",
52
+ "url": "git+https://github.com/thedaviddias/skill-check.git"
53
+ },
54
+ "license": "MIT",
55
+ "dependencies": {
56
+ "@clack/prompts": "^1.0.1",
57
+ "boxen": "^8.0.1",
58
+ "cli-table3": "^0.6.5",
59
+ "commander": "^14.0.3",
60
+ "fast-glob": "^3.3.3",
61
+ "listr2": "^10.1.0",
62
+ "minimatch": "^10.2.2",
63
+ "ora": "^9.3.0",
64
+ "picocolors": "^1.1.1",
65
+ "yaml": "^2.8.2"
66
+ },
67
+ "devDependencies": {
68
+ "@biomejs/biome": "^2.4.4",
69
+ "@commitlint/config-conventional": "^20.4.2",
70
+ "@semantic-release/changelog": "^6.0.3",
71
+ "@semantic-release/git": "^10.0.1",
72
+ "@types/node": "^25.3.0",
73
+ "@vitest/coverage-v8": "^4.0.18",
74
+ "commitlint": "^20.4.2",
75
+ "lefthook": "^2.1.1",
76
+ "semantic-release": "^25.0.3",
77
+ "tsx": "^4.21.0",
78
+ "typescript": "^5.9.3",
79
+ "vitest": "^4.0.18"
80
+ },
81
+ "packageManager": "pnpm@10.30.1+sha512.3590e550d5384caa39bd5c7c739f72270234b2f6059e13018f975c313b1eb9fefcc09714048765d4d9efe961382c312e624572c0420762bdc5d5940cdf9be73a"
82
+ }