@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.
- package/.claude-plugin/plugin.json +1 -1
- package/git-sha.json +1 -1
- package/hooks/foreground-process-gate.js +22 -3
- package/package.json +3 -1
- package/scripts/acceptance.mjs +471 -0
- package/scripts/install-rdc-skills.js +89 -8
- package/scripts/lib/assertions.mjs +25 -2
- package/scripts/lib/manifest-schema.mjs +13 -0
- package/scripts/self-test.mjs +6 -4
- package/scripts/test-guide-validator.mjs +2 -0
- package/skills/channel-formatter/SKILL.md +56 -6
- package/skills/lifeai-brochure-author/SKILL.md +2 -0
- package/skills/rdc-brochurify/SKILL.md +2 -0
- package/skills/rdc-extract-verifier-rules/SKILL.md +2 -0
- package/skills/rpms-filemap/SKILL.cloud.md +4 -0
- package/skills/rpms-filemap/SKILL.md +4 -0
- package/skills/tests/README.md +5 -0
- package/skills/tests/rdc-channel-formatter.test.json +45 -0
- package/tests/acceptance.test.mjs +42 -0
- package/tests/harness-gates.test.mjs +32 -2
- package/tests/validate-skills.js +17 -173
package/tests/validate-skills.js
CHANGED
|
@@ -1,183 +1,27 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* Compatibility wrapper for the current rdc-skills validation gate.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
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
|
|
16
|
-
const
|
|
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
|
-
|
|
53
|
-
|
|
54
|
-
const frontmatter = {};
|
|
14
|
+
const repoRoot = dirname(dirname(__filename));
|
|
15
|
+
const selfTest = join(repoRoot, "scripts", "self-test.mjs");
|
|
55
16
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}
|
|
61
|
-
}
|
|
17
|
+
const result = spawnSync(process.execPath, [selfTest, "--strict"], {
|
|
18
|
+
cwd: repoRoot,
|
|
19
|
+
stdio: "inherit",
|
|
20
|
+
});
|
|
62
21
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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
|
-
|
|
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);
|