HTML 82.1%
Python 14.6%
TypeScript 1.9%
CSS 1%
JavaScript 0.5%
1#!/usr/bin/env python32# =============================================================================3# Job·Ka — Groupe KA4# Auteur : Simon-Pierre Boucher5# Contact : contact@spboucher.ai6# Fichier : scripts/check_headers.py7# Rôle : Vérification de l'en-tête d'auteur (règle nº 1) — utilisé par le8# hook pre-commit : rejette tout fichier stagé sans en-tête9# Créé : 2026-08-17 Modifié : 2026-08-1710# =============================================================================11"""Vérifie que chaque fichier stagé porte l'en-tête d'auteur du projet.1213Usage :14 python3 scripts/check_headers.py <fichier ...> # fichiers explicites15 python3 scripts/check_headers.py --staged # fichiers stagés (hook)16"""17from __future__ import annotations1819import subprocess20import sys21from pathlib import Path2223# Extensions soumises à la règle nº 1 (code, scripts, configs, docs, tests)24CHECKED_EXTS = {".py", ".ts", ".tsx", ".js", ".jsx", ".css", ".html", ".md",25 ".sh", ".txt", ".yml", ".yaml", ".toml"}26# Fichiers exemptés : données, verrous, artefacts générés27EXEMPT_PARTS = {"node_modules", "dist", ".venv", "fixtures", "data",28 "__pycache__", ".git"}29EXEMPT_NAMES = {"package-lock.json", "LICENSE"}3031REQUIRED = ("Job·Ka — Groupe KA", "Simon-Pierre Boucher", "contact@spboucher.ai")323334def needs_header(path: Path) -> bool:35 if path.name in EXEMPT_NAMES:36 return False37 if set(path.parts) & EXEMPT_PARTS:38 return False39 return path.suffix.lower() in CHECKED_EXTS404142def has_header(path: Path) -> bool:43 try:44 head = path.read_text(encoding="utf-8", errors="ignore")[:1500]45 except OSError:46 return False47 return all(tok in head for tok in REQUIRED)484950def staged_files() -> list[Path]:51 out = subprocess.run(52 ["git", "diff", "--cached", "--name-only", "--diff-filter=ACM"],53 capture_output=True, text=True, check=True).stdout54 return [Path(l) for l in out.splitlines() if l.strip()]555657def main() -> int:58 if len(sys.argv) > 1 and sys.argv[1] == "--staged":59 targets = staged_files()60 else:61 targets = [Path(a) for a in sys.argv[1:]]62 bad = [p for p in targets63 if p.exists() and needs_header(p) and not has_header(p)]64 if bad:65 print("✗ En-tête d'auteur manquant (règle nº 1 du CLAUDE.md) :",66 file=sys.stderr)67 for p in bad:68 print(f" {p}", file=sys.stderr)69 print(" -> ajouter le bloc « Job·Ka — Groupe KA / Auteur : "70 "Simon-Pierre Boucher / contact@spboucher.ai »", file=sys.stderr)71 return 172 return 0737475if __name__ == "__main__":76 sys.exit(main())77