Files
everything-claude-code/scripts/hooks/quality-gate.js

99 lines
2.5 KiB
JavaScript
Executable File

#!/usr/bin/env node
/**
* Quality Gate Hook
*
* Runs lightweight quality checks after file edits.
* - Targets one file when file_path is provided
* - Falls back to no-op when language/tooling is unavailable
*/
'use strict';
const fs = require('fs');
const path = require('path');
const { spawnSync } = require('child_process');
const MAX_STDIN = 1024 * 1024;
let raw = '';
function run(command, args, cwd = process.cwd()) {
return spawnSync(command, args, {
cwd,
encoding: 'utf8',
env: process.env,
});
}
function log(msg) {
process.stderr.write(`${msg}\n`);
}
function maybeRunQualityGate(filePath) {
if (!filePath || !fs.existsSync(filePath)) {
return;
}
const ext = path.extname(filePath).toLowerCase();
const fix = String(process.env.ECC_QUALITY_GATE_FIX || '').toLowerCase() === 'true';
const strict = String(process.env.ECC_QUALITY_GATE_STRICT || '').toLowerCase() === 'true';
if (['.ts', '.tsx', '.js', '.jsx', '.json', '.md'].includes(ext)) {
// Prefer biome if present
if (fs.existsSync(path.join(process.cwd(), 'biome.json')) || fs.existsSync(path.join(process.cwd(), 'biome.jsonc'))) {
const args = ['biome', 'check', filePath];
if (fix) args.push('--write');
const result = run('npx', args);
if (result.status !== 0 && strict) {
log(`[QualityGate] Biome check failed for ${filePath}`);
}
return;
}
// Fallback to prettier when installed
const prettierArgs = ['prettier', '--check', filePath];
if (fix) {
prettierArgs[1] = '--write';
}
const prettier = run('npx', prettierArgs);
if (prettier.status !== 0 && strict) {
log(`[QualityGate] Prettier check failed for ${filePath}`);
}
return;
}
if (ext === '.go' && fix) {
run('gofmt', ['-w', filePath]);
return;
}
if (ext === '.py') {
const args = ['format'];
if (!fix) args.push('--check');
args.push(filePath);
const r = run('ruff', args);
if (r.status !== 0 && strict) {
log(`[QualityGate] Ruff check failed for ${filePath}`);
}
}
}
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', () => {
try {
const input = JSON.parse(raw);
const filePath = String(input.tool_input?.file_path || '');
maybeRunQualityGate(filePath);
} catch {
// Ignore parse errors.
}
process.stdout.write(raw);
});