원클릭으로
usethis-prek-hook-bespoke-create
Write bespoke prek hooks as reusable Python scripts for custom checks
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Write bespoke prek hooks as reusable Python scripts for custom checks
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
General guidelines for writing tests in the usethis project, including test class organization
Guidelines for Python code design decisions such as when to share vs. duplicate code
Create a lesson from a development difficulty, covering root cause analysis, principle generalisation, and filing as a GitHub issue
Check whether docs/config-files.txt (the machine-readable export) is in-sync with the per-tool tables in docs/about/config-files.md
Enforce version bumping, scope checking, and content quality guidelines when modifying SKILL.md files
Modify Python code (e.g. refactor, add new code, or delete code)
| name | usethis-prek-hook-bespoke-create |
| description | Write bespoke prek hooks as reusable Python scripts for custom checks |
| compatibility | usethis, prek, git, Python |
| license | MIT |
| metadata | {"version":"1.5"} |
Use this skill when creating custom prek hooks that aren't provided by an existing third-party tool.
.py script in the hooks/ directory at the project root..pre-commit-config.yaml using uv run.usethis-prek-add-hook skill's guidance.Most hooks should be written as general-purpose tools that could work in any project. Do not hard-code project-specific names, paths, section headers, or other logic into the hook script itself. Instead, accept all project-specific configuration via command-line arguments.
For example:
--name or --prefix argument.--source-root, --output-file, or similar arguments.The .pre-commit-config.yaml entry is where project-specific values belong — passed as args to the hook. The hook script itself should be reusable as-is in a different project.
Some hooks are inherently project-specific — for example, a hook that imports directly from the project's own source package. These hooks cannot reasonably be generalized and it is acceptable to hard-code project-specific values directly in the script.
When writing a project-specific hook:
Import directly from the project package rather than accepting dotted-path arguments.
Hard-code any paths or names that are specific to this project.
Exempt the hook from the check-banned-words check by passing its filename to the --ignore-files argument in .pre-commit-config.yaml:
args: ["--hooks-dir=hooks", "--ignore-files=my-project-hook.py", "usethis"]
Place scripts in the hooks/ directory at the project root. Use kebab-case for the filename with a .py extension (e.g., hooks/check-skills-documented.py).
Scripts should:
main() first, then helper functions below it. Readers should encounter the high-level logic before the implementation details.sys.exit(0) for success and sys.exit(1) for failure.stdout. The pre-commit framework captures stdout as the primary output stream and displays it to the user; output sent to stderr may be silently dropped or displayed inconsistently.Passed or Failed status. A redundant success message adds noise and can mask the absence of expected violation output.--fix as an optional flag when the hook can auto-correct issues. When --fix is passed, the hook should fix problems in-place and exit 0 if all issues were resolved.argparse for argument parsing when flags are needed.Hooks must use only the Python standard library. Do not subprocess external tools — if a check requires an external tool, it should be a separate third-party hook instead of a bespoke one.
Hooks that write files must respect the operating system's newline convention. Use os.linesep to join lines in generated content, and pass newline="" to I/O calls so Python does not double-translate line endings:
os.linesep.join(lines) + os.linesep instead of "\n".join(lines) + "\n".path.write_text(content, encoding="utf-8", newline="").open(path, encoding="utf-8", newline="") so the raw bytes are returned and can be compared accurately against the generated content.splitlines() / "\n".join() in helper functions whose output feeds into a larger render step) may keep using "\n" internally. Convert to os.linesep once, at the point where the final content is assembled for writing.Hooks run on every commit, so they must be fast:
pathlib.Path.glob() or os.scandir() instead of walking entire trees..pre-commit-config.yamlRegister the hook as a local system hook with uv run:
- repo: local
hooks:
- id: <hook-id>
name: <hook-id>
entry: uv run --frozen --offline hooks/<hook-id>.py
language: system
always_run: true
pass_filenames: false
priority: 0
Key points:
entry uses uv run --frozen --offline so the hook runs without network access and with locked dependencies.id and name should match the script filename (without .py).pass_filenames: false since bespoke hooks typically determine their own file targets.files to scope hooks to specific directories (e.g. files: ^(src|tests)/). Directory-scoped hooks silently miss violations in unexpected locations and create a false sense of coverage. Instead, use types: [python] (or other type filters) to match files by type across the entire repository, or use pass_filenames: false and let the hook script determine its own file targets via CLI arguments. If a check is worth running, it is worth running everywhere.usethis-prek-add-hook skill for priority assignment and placement within the file.