Merge aba48f90f1 into 9d5739d992
This commit is contained in:
commit
efb61da6d1
|
|
@ -42,10 +42,11 @@
|
||||||
"prepare": "command -v husky >/dev/null 2>&1 && husky || exit 0",
|
"prepare": "command -v husky >/dev/null 2>&1 && husky || exit 0",
|
||||||
"quality": "npm run format:check && npm run lint && npm run lint:md && npm run docs:build && npm run test:install && npm run test:urls && npm run validate:refs && npm run validate:skills && npm run docs:validate-sidebar",
|
"quality": "npm run format:check && npm run lint && npm run lint:md && npm run docs:build && npm run test:install && npm run test:urls && npm run validate:refs && npm run validate:skills && npm run docs:validate-sidebar",
|
||||||
"rebundle": "node tools/installer/bundlers/bundle-web.js rebundle",
|
"rebundle": "node tools/installer/bundlers/bundle-web.js rebundle",
|
||||||
"test": "npm run test:refs && npm run test:install && npm run test:urls && npm run test:channels && npm run lint && npm run lint:md && npm run format:check",
|
"test": "npm run test:refs && npm run test:install && npm run test:urls && npm run test:channels && npm run test:skills && npm run lint && npm run lint:md && npm run format:check",
|
||||||
"test:channels": "node test/test-installer-channels.js",
|
"test:channels": "node test/test-installer-channels.js",
|
||||||
"test:install": "node test/test-installation-components.js",
|
"test:install": "node test/test-installation-components.js",
|
||||||
"test:refs": "node test/test-file-refs-csv.js",
|
"test:refs": "node test/test-file-refs-csv.js",
|
||||||
|
"test:skills": "node test/test-validate-skills.js",
|
||||||
"test:urls": "node test/test-parse-source-urls.js",
|
"test:urls": "node test/test-parse-source-urls.js",
|
||||||
"validate:refs": "node tools/validate-file-refs.js --strict",
|
"validate:refs": "node tools/validate-file-refs.js --strict",
|
||||||
"validate:skills": "node tools/validate-skills.js --strict"
|
"validate:skills": "node tools/validate-skills.js --strict"
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
---
|
||||||
|
name: deprecated-shim
|
||||||
|
description: 'DEPRECATED — consolidated into bmad-foo; this skill will be removed in v7 in favor of `bmad-foo`.'
|
||||||
|
---
|
||||||
|
|
||||||
|
# DEPRECATED — forwards to bmad-foo
|
||||||
|
|
||||||
|
This skill was consolidated into `bmad-foo` and is retained as a thin compatibility
|
||||||
|
shim so existing invocations keep working. New work should invoke `bmad-foo` directly.
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
---
|
||||||
|
name: missing-trigger
|
||||||
|
description: 'Generates a thing and writes it to disk for the user.'
|
||||||
|
---
|
||||||
|
|
||||||
|
# Missing Trigger
|
||||||
|
|
||||||
|
An active (non-deprecated) skill whose description omits a "Use when" trigger phrase.
|
||||||
|
This fixture guards against regressions: SKILL-06 must still flag it.
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
---
|
||||||
|
name: with-trigger
|
||||||
|
description: 'Generates a thing and writes it to disk. Use when the user asks to scaffold a thing.'
|
||||||
|
---
|
||||||
|
|
||||||
|
# With Trigger
|
||||||
|
|
||||||
|
An active skill whose description includes a "Use when" trigger phrase.
|
||||||
|
|
@ -0,0 +1,107 @@
|
||||||
|
/**
|
||||||
|
* Skill Validation Test Runner
|
||||||
|
*
|
||||||
|
* Tests validateSkill() from validate-skills.js against fixtures, focused on
|
||||||
|
* SKILL-06 (description quality) and its deprecated-skill exemption.
|
||||||
|
*
|
||||||
|
* Usage: node test/test-validate-skills.js
|
||||||
|
* Exit codes: 0 = all tests pass, 1 = test failures
|
||||||
|
*/
|
||||||
|
|
||||||
|
const path = require('node:path');
|
||||||
|
const { validateSkill } = require('../tools/validate-skills.js');
|
||||||
|
|
||||||
|
// ANSI color codes
|
||||||
|
const colors = {
|
||||||
|
reset: '\u001B[0m',
|
||||||
|
green: '\u001B[32m',
|
||||||
|
red: '\u001B[31m',
|
||||||
|
cyan: '\u001B[36m',
|
||||||
|
dim: '\u001B[2m',
|
||||||
|
};
|
||||||
|
|
||||||
|
const FIXTURES = path.join(__dirname, 'fixtures/validate-skills');
|
||||||
|
|
||||||
|
let totalTests = 0;
|
||||||
|
let passedTests = 0;
|
||||||
|
const failures = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run a single named test case, recording the result and printing a status line.
|
||||||
|
* @param {string} name - Human-readable test description.
|
||||||
|
* @param {Function} fn - Test body; throw to signal failure.
|
||||||
|
*/
|
||||||
|
function test(name, fn) {
|
||||||
|
totalTests++;
|
||||||
|
try {
|
||||||
|
fn();
|
||||||
|
passedTests++;
|
||||||
|
console.log(` ${colors.green}✓${colors.reset} ${name}`);
|
||||||
|
} catch (error) {
|
||||||
|
console.log(` ${colors.red}✗${colors.reset} ${name} ${colors.red}${error.message}${colors.reset}`);
|
||||||
|
failures.push({ name, message: error.message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Throw an Error with `message` when `condition` is falsy.
|
||||||
|
* @param {boolean} condition - Expression that must hold.
|
||||||
|
* @param {string} message - Failure message.
|
||||||
|
*/
|
||||||
|
function assert(condition, message) {
|
||||||
|
if (!condition) throw new Error(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether validateSkill emitted the SKILL-06 "Use when/Use if" trigger finding
|
||||||
|
* for the given fixture skill directory.
|
||||||
|
* @param {string} skillName - Fixture subdirectory name under FIXTURES.
|
||||||
|
* @returns {boolean} True if the trigger-phrase finding was reported.
|
||||||
|
*/
|
||||||
|
function hasTriggerFinding(skillName) {
|
||||||
|
const findings = validateSkill(path.join(FIXTURES, skillName));
|
||||||
|
return findings.some((f) => f.rule === 'SKILL-06' && /trigger phrase/i.test(f.detail));
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`\n${colors.cyan}Skill Validation — SKILL-06 trigger phrase${colors.reset}\n`);
|
||||||
|
|
||||||
|
test('deprecated skill is exempt from the trigger-phrase requirement', () => {
|
||||||
|
assert(
|
||||||
|
hasTriggerFinding('deprecated-shim') === false,
|
||||||
|
'Expected no SKILL-06 trigger finding for a DEPRECATED skill',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('active skill missing a trigger phrase is still flagged', () => {
|
||||||
|
assert(
|
||||||
|
hasTriggerFinding('missing-trigger') === true,
|
||||||
|
'Expected a SKILL-06 trigger finding for a non-deprecated skill without "Use when"',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('active skill with a "Use when" trigger is not flagged', () => {
|
||||||
|
assert(
|
||||||
|
hasTriggerFinding('with-trigger') === false,
|
||||||
|
'Expected no SKILL-06 trigger finding when description contains "Use when"',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// --- Summary ---
|
||||||
|
console.log(`\n${colors.cyan}${'═'.repeat(55)}${colors.reset}`);
|
||||||
|
console.log(`${colors.cyan}Test Results:${colors.reset}`);
|
||||||
|
console.log(` Total: ${totalTests}`);
|
||||||
|
console.log(` Passed: ${colors.green}${passedTests}${colors.reset}`);
|
||||||
|
console.log(` Failed: ${passedTests === totalTests ? colors.green : colors.red}${totalTests - passedTests}${colors.reset}`);
|
||||||
|
console.log(`${colors.cyan}${'═'.repeat(55)}${colors.reset}\n`);
|
||||||
|
|
||||||
|
if (failures.length > 0) {
|
||||||
|
console.log(`${colors.red}FAILED TESTS:${colors.reset}\n`);
|
||||||
|
for (const failure of failures) {
|
||||||
|
console.log(`${colors.red}✗${colors.reset} ${failure.name}`);
|
||||||
|
console.log(` ${failure.message}\n`);
|
||||||
|
}
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`${colors.green}All tests passed!${colors.reset}\n`);
|
||||||
|
process.exit(0);
|
||||||
|
|
@ -312,6 +312,11 @@ function validateSkill(skillDir) {
|
||||||
const name = skillFm && skillFm.name;
|
const name = skillFm && skillFm.name;
|
||||||
const description = skillFm && skillFm.description;
|
const description = skillFm && skillFm.description;
|
||||||
|
|
||||||
|
// Deprecated skills are thin compatibility shims that forward to a replacement.
|
||||||
|
// They intentionally omit a "Use when" trigger so users are steered to the new
|
||||||
|
// skill instead, so exempt them from the SKILL-06 trigger-phrase requirement.
|
||||||
|
const isDeprecated = typeof description === 'string' && /^\s*deprecated\b/i.test(description);
|
||||||
|
|
||||||
// --- SKILL-04: name format ---
|
// --- SKILL-04: name format ---
|
||||||
if (name && !NAME_REGEX.test(name)) {
|
if (name && !NAME_REGEX.test(name)) {
|
||||||
findings.push({
|
findings.push({
|
||||||
|
|
@ -349,7 +354,7 @@ function validateSkill(skillDir) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!/use\s+when\b/i.test(description) && !/use\s+if\b/i.test(description)) {
|
if (!isDeprecated && !/use\s+when\b/i.test(description) && !/use\s+if\b/i.test(description)) {
|
||||||
findings.push({
|
findings.push({
|
||||||
rule: 'SKILL-06',
|
rule: 'SKILL-06',
|
||||||
title: 'description Quality',
|
title: 'description Quality',
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue