mirror of
https://github.com/pbakaus/impeccable.git
synced 2026-09-22 02:56:52 +03:00
Allow XML-block prose form in SKILL.md DON'T parser
Recognize "DO NOT" / "DO" lines (with optional colon) inside <rules> and <absolute_bans> blocks, and make skillGuideline substring matching case-insensitive so the validator handles the new XML-structured SKILL.md without rejecting the refactored prose. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
282987ad7b
commit
0728eaadea
+8
-4
@@ -122,10 +122,14 @@ function validateAntipatternRules(rootDir) {
|
|||||||
const antipatterns = new Function(`return [${apMatch[1]}]`)();
|
const antipatterns = new Function(`return [${apMatch[1]}]`)();
|
||||||
const { antipatterns: skillSections } = readPatterns(rootDir);
|
const { antipatterns: skillSections } = readPatterns(rootDir);
|
||||||
|
|
||||||
// Build section -> joined-DON'T-text lookup for substring matching
|
// Build section -> joined-DON'T-text lookup for substring matching.
|
||||||
|
// Lowercased for case-insensitive matching: my XML refactor uses sentence-
|
||||||
|
// case "DO NOT nest cards" while the rules' skillGuideline strings are
|
||||||
|
// sentence-cased "Nest cards inside cards" (a fragment from the original
|
||||||
|
// markdown bullet "**DON'T**: Nest cards inside cards.").
|
||||||
const sectionText = {};
|
const sectionText = {};
|
||||||
for (const section of skillSections) {
|
for (const section of skillSections) {
|
||||||
sectionText[section.name] = section.items.join('\n');
|
sectionText[section.name] = section.items.join('\n').toLowerCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
let errors = 0;
|
let errors = 0;
|
||||||
@@ -143,8 +147,8 @@ function validateAntipatternRules(rootDir) {
|
|||||||
errors++;
|
errors++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!text.includes(rule.skillGuideline)) {
|
if (!text.includes(rule.skillGuideline.toLowerCase())) {
|
||||||
console.error(` ❌ Rule '${rule.id}': skillGuideline '${rule.skillGuideline}' not found in any **DON'T** of section '${rule.skillSection}' in source/skills/impeccable/SKILL.md`);
|
console.error(` ❌ Rule '${rule.id}': skillGuideline '${rule.skillGuideline}' not found in any DON'T of section '${rule.skillSection}' in source/skills/impeccable/SKILL.md`);
|
||||||
errors++;
|
errors++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
+42
-15
@@ -215,7 +215,11 @@ export function writeFile(filePath, content) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Extract patterns from frontend-design SKILL.md
|
* Extract patterns from frontend-design SKILL.md
|
||||||
* Parses **DO**: and **DON'T**: lines, grouped by section headings
|
* Parses DO/DON'T lines grouped by section headings.
|
||||||
|
* Recognizes both formats:
|
||||||
|
* - Markdown bullet form: `**DO**: …` / `**DON'T**: …`
|
||||||
|
* - XML-block prose form: `DO …` / `DO NOT …` (used inside
|
||||||
|
* <typography_rules>, <color_rules>, <spatial_rules>, <absolute_bans>)
|
||||||
* Returns { patterns: [...], antipatterns: [...] }
|
* Returns { patterns: [...], antipatterns: [...] }
|
||||||
*/
|
*/
|
||||||
export function readPatterns(rootDir) {
|
export function readPatterns(rootDir) {
|
||||||
@@ -232,6 +236,17 @@ export function readPatterns(rootDir) {
|
|||||||
const antipatternsMap = {}; // category -> items[]
|
const antipatternsMap = {}; // category -> items[]
|
||||||
let currentSection = null;
|
let currentSection = null;
|
||||||
|
|
||||||
|
const pushPattern = (item) => {
|
||||||
|
if (!currentSection) return;
|
||||||
|
if (!patternsMap[currentSection]) patternsMap[currentSection] = [];
|
||||||
|
patternsMap[currentSection].push(item);
|
||||||
|
};
|
||||||
|
const pushAntipattern = (item) => {
|
||||||
|
if (!currentSection) return;
|
||||||
|
if (!antipatternsMap[currentSection]) antipatternsMap[currentSection] = [];
|
||||||
|
antipatternsMap[currentSection].push(item);
|
||||||
|
};
|
||||||
|
|
||||||
for (const line of lines) {
|
for (const line of lines) {
|
||||||
const trimmed = line.trim();
|
const trimmed = line.trim();
|
||||||
|
|
||||||
@@ -245,23 +260,35 @@ export function readPatterns(rootDir) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse **DO**: lines
|
// Markdown bullet form (legacy): **DO**: ... and **DON'T**: ...
|
||||||
if (trimmed.startsWith('**DO**:') && currentSection) {
|
if (trimmed.startsWith('**DO**:')) {
|
||||||
const item = trimmed.slice(7).trim();
|
pushPattern(trimmed.slice(7).trim());
|
||||||
if (!patternsMap[currentSection]) {
|
continue;
|
||||||
patternsMap[currentSection] = [];
|
}
|
||||||
}
|
if (trimmed.startsWith("**DON'T**:")) {
|
||||||
patternsMap[currentSection].push(item);
|
pushAntipattern(trimmed.slice(10).trim());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse **DON'T**: lines
|
// XML-block prose form (current). Both space and colon variants:
|
||||||
if (trimmed.startsWith("**DON'T**:") && currentSection) {
|
// "DO NOT use ..." / "DO NOT: Use ..."
|
||||||
const item = trimmed.slice(10).trim();
|
// "DO use ..." / "DO: Use ..."
|
||||||
if (!antipatternsMap[currentSection]) {
|
// IMPORTANT: check `DO NOT` BEFORE `DO` so the prefix doesn't get
|
||||||
antipatternsMap[currentSection] = [];
|
// gobbled by the wrong matcher.
|
||||||
}
|
if (trimmed.startsWith('DO NOT: ')) {
|
||||||
antipatternsMap[currentSection].push(item);
|
pushAntipattern(trimmed.slice('DO NOT: '.length).trim());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (trimmed.startsWith('DO NOT ')) {
|
||||||
|
pushAntipattern(trimmed.slice('DO NOT '.length).trim());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (trimmed.startsWith('DO: ')) {
|
||||||
|
pushPattern(trimmed.slice('DO: '.length).trim());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (trimmed.startsWith('DO ')) {
|
||||||
|
pushPattern(trimmed.slice('DO '.length).trim());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user