forked from affaan-m/everything-claude-code
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdesign-quality-check.js
More file actions
131 lines (111 loc) · 3.77 KB
/
design-quality-check.js
File metadata and controls
131 lines (111 loc) · 3.77 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
#!/usr/bin/env node
/**
* PostToolUse hook: lightweight frontend design-quality reminder.
*
* This stays self-contained inside ECC. It does not call remote models or
* install packages. The goal is to catch obviously generic UI drift and keep
* frontend edits aligned with ECC's stronger design standards.
*/
'use strict';
const fs = require('fs');
const path = require('path');
const FRONTEND_EXTENSIONS = /\.(astro|css|html|jsx|scss|svelte|tsx|vue)$/i;
const MAX_STDIN = 1024 * 1024;
const GENERIC_SIGNALS = [
{ pattern: /\bget started\b/i, label: '"Get Started" CTA copy' },
{ pattern: /\blearn more\b/i, label: '"Learn more" CTA copy' },
{ pattern: /\bgrid-cols-(3|4)\b/, label: 'uniform multi-card grid' },
{ pattern: /\bbg-gradient-to-[trbl]/, label: 'stock gradient utility usage' },
{ pattern: /\btext-center\b/, label: 'centered default layout cues' },
{ pattern: /\bfont-(sans|inter)\b/i, label: 'default font utility' },
];
const CHECKLIST = [
'visual hierarchy with real contrast',
'intentional spacing rhythm',
'depth, layering, or overlap',
'purposeful hover and focus states',
'color and typography that feel specific',
];
function getFilePaths(input) {
const toolInput = input?.tool_input || {};
if (toolInput.file_path) {
return [String(toolInput.file_path)];
}
if (Array.isArray(toolInput.edits)) {
return toolInput.edits
.map(edit => String(edit?.file_path || ''))
.filter(Boolean);
}
return [];
}
function readContent(filePath) {
try {
return fs.readFileSync(path.resolve(filePath), 'utf8');
} catch {
return '';
}
}
function detectSignals(content) {
return GENERIC_SIGNALS.filter(signal => signal.pattern.test(content)).map(signal => signal.label);
}
function buildWarning(frontendPaths, findings) {
const pathLines = frontendPaths.map(fp => ` - ${fp}`).join('\n');
const signalLines = findings.length > 0
? findings.map(item => ` - ${item}`).join('\n')
: ' - no obvious canned-template strings detected';
return [
'[Hook] DESIGN CHECK: frontend file(s) modified:',
pathLines,
'[Hook] Review for generic/template drift. Frontend should have:',
CHECKLIST.map(item => ` - ${item}`).join('\n'),
'[Hook] Heuristic signals:',
signalLines,
].join('\n');
}
function run(inputOrRaw) {
let input;
let rawInput = inputOrRaw;
try {
if (typeof inputOrRaw === 'string') {
rawInput = inputOrRaw;
input = inputOrRaw.trim() ? JSON.parse(inputOrRaw) : {};
} else {
input = inputOrRaw || {};
rawInput = JSON.stringify(inputOrRaw ?? {});
}
} catch {
return { exitCode: 0, stdout: typeof rawInput === 'string' ? rawInput : '' };
}
const filePaths = getFilePaths(input);
const frontendPaths = filePaths.filter(filePath => FRONTEND_EXTENSIONS.test(filePath));
if (frontendPaths.length === 0) {
return { exitCode: 0, stdout: typeof rawInput === 'string' ? rawInput : '' };
}
const findings = [];
for (const filePath of frontendPaths) {
const content = readContent(filePath);
findings.push(...detectSignals(content));
}
return {
exitCode: 0,
stdout: typeof rawInput === 'string' ? rawInput : '',
stderr: buildWarning(frontendPaths, findings),
};
}
module.exports = { run };
if (require.main === module) {
let raw = '';
process.stdin.setEncoding('utf8');
process.stdin.on('data', chunk => {
if (raw.length < MAX_STDIN) {
const remaining = MAX_STDIN - raw.length;
raw += chunk.substring(0, remaining);
}
});
process.stdin.on('end', () => {
const result = run(raw);
if (result.stderr) process.stderr.write(`${result.stderr}\n`);
process.stdout.write(typeof result.stdout === 'string' ? result.stdout : raw);
process.exit(Number.isInteger(result.exitCode) ? result.exitCode : 0);
});
}