@lifeaitools/rdc-skills 0.24.9 → 0.24.11

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.
@@ -1,183 +1,27 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * Validate rdc-skills skills and guides
3
+ * Compatibility wrapper for the current rdc-skills validation gate.
4
4
  *
5
- * Checks:
6
- * - YAML frontmatter exists and has required fields
7
- * - Required sections exist (## When to Use, etc.)
8
- * - Markdown is well-formed
9
- *
10
- * Exit codes:
11
- * 0 = all valid
12
- * 1 = validation failed
5
+ * The older validator required every skill to use the same "When to Use" /
6
+ * "Procedure" section shape. The canonical gate is now scripts/self-test.mjs,
7
+ * which understands the shipped skill variants, guide checks, hook behavior,
8
+ * plugin metadata, and strict warning policy.
13
9
  */
14
10
 
15
- const fs = require('fs');
16
- const path = require('path');
17
-
18
- const REQUIRED_FRONTMATTER = ['name', 'description'];
19
- const REQUIRED_SECTIONS = [
20
- '## When to Use',
21
- '## Procedure' // OR '## Arguments'
22
- ];
23
-
24
- let passed = 0;
25
- let failed = 0;
26
- const errors = [];
27
-
28
- function validateFile(filePath) {
29
- try {
30
- const contents = fs.readFileSync(filePath, 'utf8').replace(/\r\n/g, '\n');
31
- const lines = contents.split('\n');
32
-
33
- // Check frontmatter
34
- if (!lines[0].includes('---')) {
35
- errors.push(`${path.basename(filePath)}: Missing YAML frontmatter start`);
36
- return false;
37
- }
38
-
39
- let frontmatterEnd = -1;
40
- for (let i = 1; i < lines.length; i++) {
41
- if (lines[i].includes('---')) {
42
- frontmatterEnd = i;
43
- break;
44
- }
45
- }
46
-
47
- if (frontmatterEnd === -1) {
48
- errors.push(`${path.basename(filePath)}: YAML frontmatter not closed`);
49
- return false;
50
- }
11
+ const { spawnSync } = require("node:child_process");
12
+ const { join, dirname } = require("node:path");
51
13
 
52
- // Parse frontmatter
53
- const frontmatterLines = lines.slice(1, frontmatterEnd);
54
- const frontmatter = {};
14
+ const repoRoot = dirname(dirname(__filename));
15
+ const selfTest = join(repoRoot, "scripts", "self-test.mjs");
55
16
 
56
- for (const line of frontmatterLines) {
57
- const match = line.match(/^(\w+):\s*(.+)$/);
58
- if (match) {
59
- frontmatter[match[1]] = match[2];
60
- }
61
- }
17
+ const result = spawnSync(process.execPath, [selfTest, "--strict"], {
18
+ cwd: repoRoot,
19
+ stdio: "inherit",
20
+ });
62
21
 
63
- // Check required fields
64
- for (const field of REQUIRED_FRONTMATTER) {
65
- if (!frontmatter[field]) {
66
- errors.push(`${path.basename(filePath)}: Missing frontmatter field '${field}'`);
67
- return false;
68
- }
69
- }
70
-
71
- // Check required sections
72
- const bodyText = lines.slice(frontmatterEnd + 1).join('\n');
73
- const hasWhenToUse = bodyText.includes('## When to Use');
74
- const hasProcedure = bodyText.includes('## Procedure');
75
- const hasArguments = bodyText.includes('## Arguments');
76
-
77
- if (!hasWhenToUse) {
78
- errors.push(`${path.basename(filePath)}: Missing '## When to Use' section`);
79
- return false;
80
- }
81
-
82
- if (!hasProcedure && !hasArguments) {
83
- errors.push(`${path.basename(filePath)}: Missing '## Procedure' or '## Arguments' section`);
84
- return false;
85
- }
86
-
87
- return true;
88
- } catch (err) {
89
- errors.push(`${path.basename(filePath)}: ${err.message}`);
90
- return false;
91
- }
22
+ if (result.error) {
23
+ console.error(result.error.message);
24
+ process.exit(2);
92
25
  }
93
26
 
94
- function validateDirectory(dirPath, dirName) {
95
- if (!fs.existsSync(dirPath)) {
96
- console.log(`ℹ ${dirName}/ directory not found (will be populated later)`);
97
- return;
98
- }
99
-
100
- // Skills are in subdirectories: skills/<name>/SKILL.md
101
- const entries = fs.readdirSync(dirPath, { withFileTypes: true });
102
- const skillFiles = [];
103
-
104
- for (const entry of entries) {
105
- if (entry.isDirectory()) {
106
- const skillMd = path.join(dirPath, entry.name, 'SKILL.md');
107
- if (fs.existsSync(skillMd)) {
108
- skillFiles.push({ label: `${entry.name}/SKILL.md`, filePath: skillMd });
109
- }
110
- } else if (entry.name.endsWith('.md')) {
111
- skillFiles.push({ label: entry.name, filePath: path.join(dirPath, entry.name) });
112
- }
113
- }
114
-
115
- if (skillFiles.length === 0) {
116
- console.log(`ℹ ${dirName}/ (empty — will be populated later)`);
117
- return;
118
- }
119
-
120
- console.log(`\nValidating ${dirName}/`);
121
- console.log('─'.repeat(40));
122
-
123
- for (const { label, filePath } of skillFiles) {
124
- if (validateFile(filePath)) {
125
- console.log(` ✓ ${label}`);
126
- passed++;
127
- } else {
128
- console.log(` ✗ ${label}`);
129
- failed++;
130
- }
131
- }
132
- }
133
-
134
- // Main
135
- console.log('rdc-skills Validator');
136
- console.log('====================\n');
137
-
138
- const repoRoot = path.dirname(path.dirname(__filename));
139
- const skillsDir = path.join(repoRoot, 'skills');
140
- const guidesDir = path.join(repoRoot, 'guides');
141
-
142
- validateDirectory(skillsDir, 'skills');
143
-
144
- // Guides are prose docs — just check they are readable markdown files
145
- if (fs.existsSync(guidesDir)) {
146
- const guideFiles = fs.readdirSync(guidesDir).filter(f => f.endsWith('.md'));
147
- if (guideFiles.length > 0) {
148
- console.log('\nValidating guides/ (readability only)');
149
- console.log('─'.repeat(40));
150
- for (const file of guideFiles) {
151
- try {
152
- fs.readFileSync(path.join(guidesDir, file), 'utf8');
153
- console.log(` ✓ ${file}`);
154
- passed++;
155
- } catch (err) {
156
- errors.push(`${file}: ${err.message}`);
157
- console.log(` ✗ ${file}`);
158
- failed++;
159
- }
160
- }
161
- }
162
- }
163
-
164
- console.log('\n' + '═'.repeat(40));
165
- if (errors.length > 0) {
166
- console.log('\nErrors:');
167
- for (const err of errors) {
168
- console.log(` • ${err}`);
169
- }
170
- }
171
-
172
- console.log(`\nResults: ${passed} passed, ${failed} failed`);
173
-
174
- if (failed === 0 && passed > 0) {
175
- console.log('✓ All files valid\n');
176
- process.exit(0);
177
- } else if (failed === 0 && passed === 0) {
178
- console.log('ℹ No files to validate (plugin base not yet populated)\n');
179
- process.exit(0);
180
- } else {
181
- console.log('✗ Validation failed\n');
182
- process.exit(1);
183
- }
27
+ process.exit(result.status ?? 1);