Files
gstack/scripts/skill-check.ts
T
Garry Tan 4a77cc2c34 feat: /plan-design-review + /qa-design-review skills (v0.5.0) (#102)
* feat: add {{DESIGN_METHODOLOGY}} resolver and register design review skills

Add generateDesignMethodology() to gen-skill-docs.ts with 10-category, 80-item
design audit checklist. Register plan-design-review and qa-design-review templates
in findTemplates(). Add both skills to skill-check.ts SKILL_FILES. Add command
and snapshot flag validation tests for both skills in skill-validation.test.ts.

* feat: add /plan-design-review and /qa-design-review skills

/plan-design-review: report-only designer audit with letter grades, AI slop
scoring, structured first impression, design system extraction, DESIGN.md
inference and export offer. Never modifies code.

/qa-design-review: same audit, then iterative fix loop with style(design):
commits, CSS-safe WTF heuristic, before/after screenshots, final re-audit.

* chore: bump version and changelog (v0.5.0)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: update README, ARCHITECTURE for design review skills (v0.5.0)

- Update skill count to 11, add /plan-design-review and /qa-design-review
  to skill table, install/uninstall commands, and demo walkthrough
- Add narrative sections: "senior designer mode" and "designer who codes mode"
  with compelling examples showing AI Slop detection and design system inference
- Add {{DESIGN_METHODOLOGY}} to ARCHITECTURE.md placeholder table
- Extend demo to show full plan→eng→review→ship→qa→design-review pipeline

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore: regenerate design review SKILL.md files after merge from main

Picks up BASE_BRANCH_DETECT resolver and updated contributor mode from main.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: add /design-consultation skill — design consultant that creates DESIGN.md

6-phase consultant flow: product context → competitive research (WebSearch) →
complete coherent proposal → drill-downs on demand → font+color preview page →
write DESIGN.md + update CLAUDE.md. Opinionated recommendations grounded in
product context, not menu-driven forms.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* test: add E2E tests for design skill family (7 tests + LLM quality judge)

Tests 1-4: /design-consultation (core flow, research integration, existing
DESIGN.md handling, font+color preview generation).
Tests 5-6: /plan-design-review (audit report, DESIGN.md export).
Test 7: /qa-design-review (audit + fix loop).
LLM judge validates font blacklist compliance, coherence, and AI slop avoidance.
Also adds plan-design-review + qa-design-review to ALL_SKILLS test array.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: mark /design-consultation as shipped in TODOS.md

Renamed from /setup-design-md to reflect the consultant approach.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 21:55:07 -05:00

117 lines
3.9 KiB
TypeScript

#!/usr/bin/env bun
/**
* skill:check — Health summary for all SKILL.md files.
*
* Reports:
* - Command validation (valid/invalid/snapshot errors)
* - Template coverage (which SKILL.md files have .tmpl sources)
* - Freshness check (generated files match committed files)
*/
import { validateSkill } from '../test/helpers/skill-parser';
import * as fs from 'fs';
import * as path from 'path';
import { execSync } from 'child_process';
const ROOT = path.resolve(import.meta.dir, '..');
// Find all SKILL.md files
const SKILL_FILES = [
'SKILL.md',
'browse/SKILL.md',
'qa/SKILL.md',
'qa-only/SKILL.md',
'ship/SKILL.md',
'review/SKILL.md',
'retro/SKILL.md',
'plan-ceo-review/SKILL.md',
'plan-eng-review/SKILL.md',
'setup-browser-cookies/SKILL.md',
'plan-design-review/SKILL.md',
'qa-design-review/SKILL.md',
'gstack-upgrade/SKILL.md',
'document-release/SKILL.md',
].filter(f => fs.existsSync(path.join(ROOT, f)));
let hasErrors = false;
// ─── Skills ─────────────────────────────────────────────────
console.log(' Skills:');
for (const file of SKILL_FILES) {
const fullPath = path.join(ROOT, file);
const result = validateSkill(fullPath);
if (result.warnings.length > 0) {
console.log(` \u26a0\ufe0f ${file.padEnd(30)}${result.warnings.join(', ')}`);
continue;
}
const totalValid = result.valid.length;
const totalInvalid = result.invalid.length;
const totalSnapErrors = result.snapshotFlagErrors.length;
if (totalInvalid > 0 || totalSnapErrors > 0) {
hasErrors = true;
console.log(` \u274c ${file.padEnd(30)}${totalValid} valid, ${totalInvalid} invalid, ${totalSnapErrors} snapshot errors`);
for (const inv of result.invalid) {
console.log(` line ${inv.line}: unknown command '${inv.command}'`);
}
for (const se of result.snapshotFlagErrors) {
console.log(` line ${se.command.line}: ${se.error}`);
}
} else {
console.log(` \u2705 ${file.padEnd(30)}${totalValid} commands, all valid`);
}
}
// ─── Templates ──────────────────────────────────────────────
console.log('\n Templates:');
const TEMPLATES = [
{ tmpl: 'SKILL.md.tmpl', output: 'SKILL.md' },
{ tmpl: 'browse/SKILL.md.tmpl', output: 'browse/SKILL.md' },
];
for (const { tmpl, output } of TEMPLATES) {
const tmplPath = path.join(ROOT, tmpl);
const outPath = path.join(ROOT, output);
if (!fs.existsSync(tmplPath)) {
console.log(` \u26a0\ufe0f ${output.padEnd(30)} — no template`);
continue;
}
if (!fs.existsSync(outPath)) {
hasErrors = true;
console.log(` \u274c ${output.padEnd(30)} — generated file missing! Run: bun run gen:skill-docs`);
continue;
}
console.log(` \u2705 ${tmpl.padEnd(30)} \u2192 ${output}`);
}
// Skills without templates
for (const file of SKILL_FILES) {
const tmplPath = path.join(ROOT, file + '.tmpl');
if (!fs.existsSync(tmplPath) && !TEMPLATES.some(t => t.output === file)) {
console.log(` \u26a0\ufe0f ${file.padEnd(30)} — no template (OK if no $B commands)`);
}
}
// ─── Freshness ──────────────────────────────────────────────
console.log('\n Freshness:');
try {
execSync('bun run scripts/gen-skill-docs.ts --dry-run', { cwd: ROOT, stdio: 'pipe' });
console.log(' \u2705 All generated files are fresh');
} catch (err: any) {
hasErrors = true;
const output = err.stdout?.toString() || '';
console.log(' \u274c Generated files are stale:');
for (const line of output.split('\n').filter((l: string) => l.startsWith('STALE'))) {
console.log(` ${line}`);
}
console.log(' Run: bun run gen:skill-docs');
}
console.log('');
process.exit(hasErrors ? 1 : 0);