
Commit
Run staged-change-only lint and format checks before git commit so untouched files never block a clean diff.
Install
npx skills add https://github.com/gupsammy/claudest --skill commitWhat is this skill?
- Auto-detects project type from root markers: Cargo.toml, package.json, pyproject.toml
- Validators scoped to staged files—pre-existing issues in untouched paths do not fail the commit
- Scope modes: per-file lists, package dirs, and extension-gated project-wide npm lint
- Exit codes 0 pass, 1 fail, 2 no applicable validator
- Supports text or JSON output for hook and agent consumption
Adoption & trust: 1 installs on skills.sh; 253 GitHub stars; 3/3 security scanners passed (skills.sh audits); trending (+100% hot-view momentum).
Recommended Skills
Triagemattpocock/skills
Caveman Commitjuliusbrussee/caveman
Using Git Worktreesobra/superpowers
Finishing A Development Branchobra/superpowers
Git Commitgithub/awesome-copilot
Git Guardrails Claude Codemattpocock/skills
Journey fit
Primary fit
Canonical on Ship review because the skill’s purpose is pre-commit quality gates immediately before changes enter version control. Review matches scoped validators (cargo fmt, ruff, npm lint) that judge only what you staged—not whole-repo CI.
Common Questions / FAQ
Is Commit safe to install?
skills.sh reports 3 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.
SKILL.md
READMESKILL.md - Commit
#!/usr/bin/env python3 """Run project validation before committing. Detects project type from the root directory and runs the appropriate linter or build check scoped to the user's staged changes — not the whole project. Pre-existing issues in untouched files never block a commit whose diff is clean. Exit codes: 0 — validation passed 1 — validation failed (see output for details) 2 — no validator applies (no marker file, or no relevant staged files) Usage: validate.py <project-root> [--output text|json] """ from __future__ import annotations import argparse import json import os import subprocess import sys from pathlib import Path # scope semantics: # "files" — pass filtered staged file list after cmd_prefix # "dirs" — pass unique parent dirs of staged files (go-style packages) # "gated" — run project-scope cmd only if any staged file matches extensions VALIDATORS = [ { "marker": "Cargo.toml", "tool": "cargo", "scope": "files", "extensions": [".rs"], "cmd_prefix": ["cargo", "fmt", "--check", "--"], }, { "marker": "package.json", "tool": "npm", "scope": "gated", "extensions": [".js", ".jsx", ".ts", ".tsx", ".mjs", ".cjs"], "cmd": ["npm", "run", "lint", "--if-present"], }, { "marker": "pyproject.toml", "tool": "ruff", "scope": "files", "extensions": [".py"], "cmd_prefix": ["ruff", "check"], }, { "marker": "go.mod", "tool": "go", "scope": "dirs", "extensions": [".go"], "cmd_prefix": ["go", "vet"], }, { "marker": "Gemfile", "tool": "rubocop", "scope": "files", "extensions": [".rb"], "cmd_prefix": ["bundle", "exec", "rubocop", "--no-color"], "fallback_prefix": ["rubocop", "--no-color"], }, { "marker": "pom.xml", "tool": "maven", "scope": "gated", "extensions": [".java"], "cmd": ["mvn", "validate", "-q"], }, { "marker": "mix.exs", "tool": "mix", "scope": "files", "extensions": [".ex", ".exs"], "cmd_prefix": ["mix", "format", "--check-formatted"], }, { "marker": "composer.json", "tool": "composer", "scope": "gated", "extensions": ["composer.json", "composer.lock"], "cmd": ["composer", "validate", "--strict"], }, ] def get_staged_files(root: Path) -> list[str]: """Return staged file paths relative to root. Empty list if git unavailable. Uses --diff-filter=ACMR so renamed+edited files (git mv) reach validators under their new path; otherwise R entries silently skip validation. """ try: result = subprocess.run( ["git", "diff", "--cached", "--name-only", "--diff-filter=ACMR"], cwd=str(root), capture_output=True, text=True, timeout=30, ) if result.returncode != 0: if result.stderr.strip(): print(f"git diff failed: {result.stderr.strip()}", file=sys.stderr) return [] return [line.strip() for line in result.stdout.splitlines() if line.strip()] except subprocess.TimeoutExpired: print("git diff timed out after 30s", file=sys.stderr) return [] except FileNotFoundError: return [] def match_extension(path: str, extensions: list[str]) -> bool: """True if path matches any extension entry. Entries starting with '.' are treated as suffixes (e.g. '.py' matches any *.py). Other entries are treated as exact basenames (e.g. 'composer.json' matches only a file literally named composer.json, not vendor/mycomposer.json). """ basename = os.path.basename(path) for ext in extensions: if ext.startswith("."): if path.endswith(ext): return True else: if basename == ext: