Skip to main content

claude_native/
hooks.rs

1use std::fs;
2use std::path::Path;
3
4use anyhow::Result;
5use owo_colors::OwoColorize;
6
7// ── Public API ──────────────────────────────────────────────────────
8
9/// Generate and install a pre-commit hook that checks claude-native score.
10pub fn install_hook(root: &Path, min_score: u32) -> Result<()> {
11    let hooks_dir = root.join(".git").join("hooks");
12    if !hooks_dir.exists() {
13        anyhow::bail!("Not a git repository (no .git/hooks). Run `git init` first.");
14    }
15
16    let hook_path = hooks_dir.join("pre-commit");
17    let script = generate_hook_script(min_score);
18    fs::write(&hook_path, script)?;
19
20    #[cfg(unix)]
21    {
22        use std::os::unix::fs::PermissionsExt;
23        fs::set_permissions(&hook_path, fs::Permissions::from_mode(0o755))?;
24    }
25
26    println!("{}", "Pre-commit hook installed!".green().bold());
27    println!("  Path: {}", hook_path.display());
28    println!("  Min score: {min_score}");
29    println!();
30    println!("Commits will be blocked if claude-native score drops below {min_score}.");
31    Ok(())
32}
33
34/// Print the pre-commit config for .pre-commit-config.yaml integration.
35pub fn print_precommit_config() {
36    println!("Add to .pre-commit-config.yaml:");
37    println!();
38    println!("  - repo: local");
39    println!("    hooks:");
40    println!("      - id: claude-native");
41    println!("        name: Claude Native Score Check");
42    println!("        entry: claude-native");
43    println!("        language: system");
44    println!("        pass_filenames: false");
45    println!("        always_run: true");
46}
47
48// ── Private helpers ─────────────────────────────────────────────────
49
50fn generate_hook_script(min_score: u32) -> String {
51    format!(
52        r#"#!/bin/sh
53# claude-native pre-commit hook
54# Blocks commits if score drops below {min_score}
55
56if ! command -v claude-native &> /dev/null; then
57    echo "claude-native not found. Install: cargo install claude-native"
58    exit 0
59fi
60
61SCORE=$(claude-native -o json 2>/dev/null | grep '"score"' | head -1 | sed 's/[^0-9.]//g' | cut -d. -f1)
62
63if [ -z "$SCORE" ]; then
64    echo "claude-native: could not determine score, skipping check"
65    exit 0
66fi
67
68if [ "$SCORE" -lt {min_score} ]; then
69    echo ""
70    echo "claude-native: score $SCORE is below minimum {min_score}"
71    echo "Run 'claude-native' to see suggestions, or 'claude-native --fix' to auto-repair."
72    echo ""
73    exit 1
74fi
75
76echo "claude-native: score $SCORE (>= {min_score}) OK"
77exit 0
78"#
79    )
80}