From b5c395ed876a0cd12a0519b1bde95b7067075354 Mon Sep 17 00:00:00 2001 From: Ismar Iljazovic Date: Sat, 7 Feb 2026 21:04:47 +0100 Subject: [PATCH 1/2] fix: clarify `prek list` help text and add `list-builtins` command Fix misleading help text that says 'List available hooks' when it actually lists hooks configured in the current project. Add `prek list-builtins` command that lists all built-in hooks bundled with prek, with support for verbose and JSON output formats. Works from any directory without requiring a config file. Closes j178/prek#1599 --- crates/prek/src/cli/list_builtins.rs | 62 +++++++ crates/prek/src/cli/mod.rs | 13 +- crates/prek/src/hooks/builtin_hooks/mod.rs | 12 +- crates/prek/src/main.rs | 5 + crates/prek/tests/common/mod.rs | 6 + crates/prek/tests/list_builtins.rs | 187 +++++++++++++++++++++ crates/prek/tests/run.rs | 3 +- docs/cli.md | 41 ++++- 8 files changed, 324 insertions(+), 5 deletions(-) create mode 100644 crates/prek/src/cli/list_builtins.rs create mode 100644 crates/prek/tests/list_builtins.rs diff --git a/crates/prek/src/cli/list_builtins.rs b/crates/prek/src/cli/list_builtins.rs new file mode 100644 index 00000000..c9189fe4 --- /dev/null +++ b/crates/prek/src/cli/list_builtins.rs @@ -0,0 +1,62 @@ +use std::fmt::Write; + +use owo_colors::OwoColorize; +use serde::Serialize; +use strum::IntoEnumIterator; + +use crate::cli::{ExitStatus, ListOutputFormat}; +use crate::config::BuiltinHook; +use crate::hooks::BuiltinHooks; +use crate::printer::Printer; + +#[derive(Serialize)] +struct SerializableBuiltinHook { + id: String, + name: String, + description: Option, +} + +pub(crate) fn list_builtins( + output_format: ListOutputFormat, + verbose: bool, + printer: Printer, +) -> anyhow::Result { + let hooks: Vec<_> = BuiltinHooks::iter() + .filter_map(|variant| { + let id = variant.as_ref(); + BuiltinHook::from_id(id).ok() + }) + .collect(); + + match output_format { + ListOutputFormat::Text => { + if verbose { + for hook in &hooks { + writeln!(printer.stdout(), "{}", hook.id.bold())?; + if let Some(description) = &hook.options.description { + writeln!(printer.stdout(), " {description}")?; + } + writeln!(printer.stdout())?; + } + } else { + for hook in &hooks { + writeln!(printer.stdout(), "{}", hook.id)?; + } + } + } + ListOutputFormat::Json => { + let serializable: Vec<_> = hooks + .into_iter() + .map(|h| SerializableBuiltinHook { + id: h.id, + name: h.name, + description: h.options.description, + }) + .collect(); + let json_output = serde_json::to_string_pretty(&serializable)?; + writeln!(printer.stdout(), "{json_output}")?; + } + } + + Ok(ExitStatus::Success) +} diff --git a/crates/prek/src/cli/mod.rs b/crates/prek/src/cli/mod.rs index 371b16e3..abbf5ac9 100644 --- a/crates/prek/src/cli/mod.rs +++ b/crates/prek/src/cli/mod.rs @@ -20,6 +20,7 @@ mod hook_impl; mod identify; mod install; mod list; +mod list_builtins; pub mod reporter; pub mod run; mod sample_config; @@ -38,6 +39,7 @@ pub(crate) use hook_impl::hook_impl; pub(crate) use identify::identify; pub(crate) use install::{init_template_dir, install, install_hooks, uninstall}; pub(crate) use list::list; +pub(crate) use list_builtins::list_builtins; pub(crate) use run::run; pub(crate) use sample_config::sample_config; #[cfg(feature = "self-update")] @@ -212,8 +214,10 @@ pub(crate) enum Command { InstallHooks(InstallHooksArgs), /// Run hooks. Run(Box), - /// List available hooks. + /// List hooks configured in the current project. List(ListArgs), + /// List all built-in hooks bundled with prek. + ListBuiltins(ListBuiltinsArgs), /// Uninstall prek from git hooks. Uninstall(UninstallArgs), /// Validate configuration files (prek.toml or .pre-commit-config.yaml). @@ -518,6 +522,13 @@ pub(crate) enum IdentifyOutputFormat { Json, } +#[derive(Debug, Clone, Default, Args)] +pub(crate) struct ListBuiltinsArgs { + /// The output format. + #[arg(long, value_enum, default_value_t = ListOutputFormat::Text)] + pub(crate) output_format: ListOutputFormat, +} + #[derive(Debug, Clone, Default, Args)] pub(crate) struct ListArgs { /// Include the specified hooks or projects. diff --git a/crates/prek/src/hooks/builtin_hooks/mod.rs b/crates/prek/src/hooks/builtin_hooks/mod.rs index 92c0360a..9271d6d3 100644 --- a/crates/prek/src/hooks/builtin_hooks/mod.rs +++ b/crates/prek/src/hooks/builtin_hooks/mod.rs @@ -11,7 +11,17 @@ use crate::store::Store; mod check_json5; -#[derive(Debug, Copy, Clone, PartialEq, Eq, strum::AsRefStr, strum::Display, strum::EnumString)] +#[derive( + Debug, + Copy, + Clone, + PartialEq, + Eq, + strum::AsRefStr, + strum::Display, + strum::EnumIter, + strum::EnumString, +)] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] #[cfg_attr(feature = "schemars", schemars(rename_all = "kebab-case"))] #[strum(serialize_all = "kebab-case")] diff --git a/crates/prek/src/main.rs b/crates/prek/src/main.rs index 611536b3..c8da2cd7 100644 --- a/crates/prek/src/main.rs +++ b/crates/prek/src/main.rs @@ -301,6 +301,11 @@ async fn run(cli: Cli) -> Result { ) .await } + Command::ListBuiltins(args) => { + show_settings!(args); + + cli::list_builtins(args.output_format, cli.globals.verbose > 0, printer) + } Command::HookImpl(args) => { show_settings!(args); diff --git a/crates/prek/tests/common/mod.rs b/crates/prek/tests/common/mod.rs index e8d07785..74367fed 100644 --- a/crates/prek/tests/common/mod.rs +++ b/crates/prek/tests/common/mod.rs @@ -225,6 +225,12 @@ impl TestContext { command } + pub fn list_builtins(&self) -> Command { + let mut command = self.command(); + command.arg("list-builtins"); + command + } + pub fn auto_update(&self) -> Command { let mut cmd = self.command(); cmd.arg("auto-update"); diff --git a/crates/prek/tests/list_builtins.rs b/crates/prek/tests/list_builtins.rs new file mode 100644 index 00000000..c35f1ba4 --- /dev/null +++ b/crates/prek/tests/list_builtins.rs @@ -0,0 +1,187 @@ +use crate::common::{TestContext, cmd_snapshot}; + +mod common; + +#[test] +fn list_builtins_basic() { + let context = TestContext::new(); + + cmd_snapshot!(context.filters(), context.list_builtins(), @r" + success: true + exit_code: 0 + ----- stdout ----- + check-added-large-files + check-case-conflict + check-executables-have-shebangs + check-json + check-json5 + check-merge-conflict + check-symlinks + check-toml + check-xml + check-yaml + detect-private-key + end-of-file-fixer + fix-byte-order-marker + mixed-line-ending + no-commit-to-branch + trailing-whitespace + + ----- stderr ----- + "); +} + +#[test] +fn list_builtins_verbose() { + let context = TestContext::new(); + + cmd_snapshot!(context.filters(), context.list_builtins().arg("--verbose"), @r" + success: true + exit_code: 0 + ----- stdout ----- + check-added-large-files + prevents giant files from being committed. + + check-case-conflict + checks for files that would conflict in case-insensitive filesystems + + check-executables-have-shebangs + ensures that (non-binary) executables have a shebang. + + check-json + checks json files for parseable syntax. + + check-json5 + checks json5 files for parseable syntax. + + check-merge-conflict + checks for files that contain merge conflict strings. + + check-symlinks + checks for symlinks which do not point to anything. + + check-toml + checks toml files for parseable syntax. + + check-xml + checks xml files for parseable syntax. + + check-yaml + checks yaml files for parseable syntax. + + detect-private-key + detects the presence of private keys. + + end-of-file-fixer + ensures that a file is either empty, or ends with one newline. + + fix-byte-order-marker + removes utf-8 byte order marker. + + mixed-line-ending + replaces or checks mixed line ending. + + no-commit-to-branch + + trailing-whitespace + trims trailing whitespace. + + + ----- stderr ----- + "); +} + +#[test] +fn list_builtins_json() { + let context = TestContext::new(); + + cmd_snapshot!(context.filters(), context.list_builtins().arg("--output-format=json"), @r#" + success: true + exit_code: 0 + ----- stdout ----- + [ + { + "id": "check-added-large-files", + "name": "check for added large files", + "description": "prevents giant files from being committed." + }, + { + "id": "check-case-conflict", + "name": "check for case conflicts", + "description": "checks for files that would conflict in case-insensitive filesystems" + }, + { + "id": "check-executables-have-shebangs", + "name": "check that executables have shebangs", + "description": "ensures that (non-binary) executables have a shebang." + }, + { + "id": "check-json", + "name": "check json", + "description": "checks json files for parseable syntax." + }, + { + "id": "check-json5", + "name": "check json5", + "description": "checks json5 files for parseable syntax." + }, + { + "id": "check-merge-conflict", + "name": "check for merge conflicts", + "description": "checks for files that contain merge conflict strings." + }, + { + "id": "check-symlinks", + "name": "check for broken symlinks", + "description": "checks for symlinks which do not point to anything." + }, + { + "id": "check-toml", + "name": "check toml", + "description": "checks toml files for parseable syntax." + }, + { + "id": "check-xml", + "name": "check xml", + "description": "checks xml files for parseable syntax." + }, + { + "id": "check-yaml", + "name": "check yaml", + "description": "checks yaml files for parseable syntax." + }, + { + "id": "detect-private-key", + "name": "detect private key", + "description": "detects the presence of private keys." + }, + { + "id": "end-of-file-fixer", + "name": "fix end of files", + "description": "ensures that a file is either empty, or ends with one newline." + }, + { + "id": "fix-byte-order-marker", + "name": "fix utf-8 byte order marker", + "description": "removes utf-8 byte order marker." + }, + { + "id": "mixed-line-ending", + "name": "mixed line ending", + "description": "replaces or checks mixed line ending." + }, + { + "id": "no-commit-to-branch", + "name": "don't commit to branch", + "description": null + }, + { + "id": "trailing-whitespace", + "name": "trim trailing whitespace", + "description": "trims trailing whitespace." + } + ] + + ----- stderr ----- + "#); +} diff --git a/crates/prek/tests/run.rs b/crates/prek/tests/run.rs index 1f6fdb6f..73fbd786 100644 --- a/crates/prek/tests/run.rs +++ b/crates/prek/tests/run.rs @@ -2171,7 +2171,8 @@ fn selectors_completion() -> Result<()> { install Install prek as a git hook under the `.git/hooks/` directory install-hooks Create environments for all hooks used in the config file run Run hooks - list List available hooks + list List hooks configured in the current project + list-builtins List all built-in hooks bundled with prek uninstall Uninstall prek from git hooks validate-config Validate configuration files (prek.toml or .pre-commit-config.yaml) validate-manifest Validate `.pre-commit-hooks.yaml` files diff --git a/docs/cli.md b/docs/cli.md index 0f649809..07ef24cb 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -15,7 +15,8 @@ prek [OPTIONS] [HOOK|PROJECT]... [COMMAND]
prek install

Install prek as a git hook under the .git/hooks/ directory

prek install-hooks

Create environments for all hooks used in the config file

prek run

Run hooks

-
prek list

List available hooks

+
prek list

List hooks configured in the current project

+
prek list-builtins

List all built-in hooks bundled with prek

prek uninstall

Uninstall prek from git hooks

prek validate-config

Validate configuration files (prek.toml or .pre-commit-config.yaml)

prek validate-manifest

Validate .pre-commit-hooks.yaml files

@@ -264,7 +265,7 @@ prek run [OPTIONS] [HOOK|PROJECT]... ## prek list -List available hooks +List hooks configured in the current project

Usage

@@ -369,6 +370,42 @@ prek list [OPTIONS] [HOOK|PROJECT]...
--version, -V

Display the prek version

+## prek list-builtins + +List all built-in hooks bundled with prek + +

Usage

+ +``` +prek list-builtins [OPTIONS] +``` + +

Options

+ +
--cd, -C dir

Change to directory before running

+
--color color

Whether to use color in output

+

May also be set with the PREK_COLOR environment variable.

[default: auto]

Possible values:

+
    +
  • auto: Enables colored output only when the output is going to a terminal or TTY with support
  • +
  • always: Enables colored output regardless of the detected environment
  • +
  • never: Disables colored output
  • +
--config, -c config

Path to alternate config file

+
--help, -h

Display the concise help for this command

+
--log-file log-file

Write trace logs to the specified file. If not specified, trace logs will be written to $PREK_HOME/prek.log

+
--no-progress

Hide all progress outputs.

+

For example, spinners or progress bars.

+
--output-format output-format

The output format

+

[default: text]

Possible values:

+
    +
  • text
  • +
  • json
  • +
--quiet, -q

Use quiet output.

+

Repeating this option, e.g., -qq, will enable a silent mode in which prek will write no output to stdout.

+

May also be set with the PREK_QUIET environment variable.

--refresh

Refresh all cached data

+
--verbose, -v

Use verbose output

+
--version, -V

Display the prek version

+
+ ## prek uninstall Uninstall prek from git hooks From 08a2e4c47dba69c53bf51a4963ff2c4ce38cb227 Mon Sep 17 00:00:00 2001 From: Jo <10510431+j178@users.noreply.github.com> Date: Sun, 8 Feb 2026 13:54:23 +0800 Subject: [PATCH 2/2] Move to `prek util list-builtins` --- crates/prek/src/cli/list_builtins.rs | 16 +++--- crates/prek/src/cli/mod.rs | 4 +- crates/prek/src/main.rs | 10 ++-- crates/prek/tests/common/mod.rs | 6 --- crates/prek/tests/list_builtins.rs | 6 +-- crates/prek/tests/run.rs | 1 - docs/cli.md | 74 ++++++++++++++-------------- 7 files changed, 54 insertions(+), 63 deletions(-) diff --git a/crates/prek/src/cli/list_builtins.rs b/crates/prek/src/cli/list_builtins.rs index c9189fe4..44260af7 100644 --- a/crates/prek/src/cli/list_builtins.rs +++ b/crates/prek/src/cli/list_builtins.rs @@ -16,22 +16,21 @@ struct SerializableBuiltinHook { description: Option, } +/// List all builtin hooks. pub(crate) fn list_builtins( output_format: ListOutputFormat, verbose: bool, printer: Printer, ) -> anyhow::Result { - let hooks: Vec<_> = BuiltinHooks::iter() - .filter_map(|variant| { - let id = variant.as_ref(); - BuiltinHook::from_id(id).ok() - }) - .collect(); + let hooks = BuiltinHooks::iter().map(|variant| { + let id = variant.as_ref(); + BuiltinHook::from_id(id).expect("All BuiltinHooks variants should be valid") + }); match output_format { ListOutputFormat::Text => { if verbose { - for hook in &hooks { + for hook in hooks { writeln!(printer.stdout(), "{}", hook.id.bold())?; if let Some(description) = &hook.options.description { writeln!(printer.stdout(), " {description}")?; @@ -39,14 +38,13 @@ pub(crate) fn list_builtins( writeln!(printer.stdout())?; } } else { - for hook in &hooks { + for hook in hooks { writeln!(printer.stdout(), "{}", hook.id)?; } } } ListOutputFormat::Json => { let serializable: Vec<_> = hooks - .into_iter() .map(|h| SerializableBuiltinHook { id: h.id, name: h.name, diff --git a/crates/prek/src/cli/mod.rs b/crates/prek/src/cli/mod.rs index 1401b404..7c37930b 100644 --- a/crates/prek/src/cli/mod.rs +++ b/crates/prek/src/cli/mod.rs @@ -216,8 +216,6 @@ pub(crate) enum Command { Run(Box), /// List hooks configured in the current workspace. List(ListArgs), - /// List all built-in hooks bundled with prek. - ListBuiltins(ListBuiltinsArgs), /// Uninstall prek from git hooks. Uninstall(UninstallArgs), /// Validate configuration files (prek.toml or .pre-commit-config.yaml). @@ -733,6 +731,8 @@ pub(crate) struct UtilNamespace { pub(crate) enum UtilCommand { /// Show file identification tags. Identify(IdentifyArgs), + /// List all built-in hooks bundled with prek. + ListBuiltins(ListBuiltinsArgs), /// Install hook script in a directory intended for use with `git config init.templateDir`. #[command(alias = "init-templatedir")] InitTemplateDir(InitTemplateDirArgs), diff --git a/crates/prek/src/main.rs b/crates/prek/src/main.rs index aeffd67b..4b5b2d90 100644 --- a/crates/prek/src/main.rs +++ b/crates/prek/src/main.rs @@ -301,11 +301,6 @@ async fn run(cli: Cli) -> Result { ) .await } - Command::ListBuiltins(args) => { - show_settings!(args); - - cli::list_builtins(args.output_format, cli.globals.verbose > 0, printer) - } Command::HookImpl(args) => { show_settings!(args); @@ -389,6 +384,11 @@ async fn run(cli: Cli) -> Result { cli::identify(&args.paths, args.output_format, printer) } + UtilCommand::ListBuiltins(args) => { + show_settings!(args); + + cli::list_builtins(args.output_format, cli.globals.verbose > 0, printer) + } UtilCommand::InitTemplateDir(args) => { show_settings!(args); diff --git a/crates/prek/tests/common/mod.rs b/crates/prek/tests/common/mod.rs index 74367fed..e8d07785 100644 --- a/crates/prek/tests/common/mod.rs +++ b/crates/prek/tests/common/mod.rs @@ -225,12 +225,6 @@ impl TestContext { command } - pub fn list_builtins(&self) -> Command { - let mut command = self.command(); - command.arg("list-builtins"); - command - } - pub fn auto_update(&self) -> Command { let mut cmd = self.command(); cmd.arg("auto-update"); diff --git a/crates/prek/tests/list_builtins.rs b/crates/prek/tests/list_builtins.rs index c35f1ba4..323c2ed0 100644 --- a/crates/prek/tests/list_builtins.rs +++ b/crates/prek/tests/list_builtins.rs @@ -6,7 +6,7 @@ mod common; fn list_builtins_basic() { let context = TestContext::new(); - cmd_snapshot!(context.filters(), context.list_builtins(), @r" + cmd_snapshot!(context.filters(), context.command().arg("util").arg("list-builtins"), @r" success: true exit_code: 0 ----- stdout ----- @@ -35,7 +35,7 @@ fn list_builtins_basic() { fn list_builtins_verbose() { let context = TestContext::new(); - cmd_snapshot!(context.filters(), context.list_builtins().arg("--verbose"), @r" + cmd_snapshot!(context.filters(), context.command().arg("util").arg("list-builtins").arg("--verbose"), @r" success: true exit_code: 0 ----- stdout ----- @@ -95,7 +95,7 @@ fn list_builtins_verbose() { fn list_builtins_json() { let context = TestContext::new(); - cmd_snapshot!(context.filters(), context.list_builtins().arg("--output-format=json"), @r#" + cmd_snapshot!(context.filters(), context.command().arg("util").arg("list-builtins").arg("--output-format=json"), @r#" success: true exit_code: 0 ----- stdout ----- diff --git a/crates/prek/tests/run.rs b/crates/prek/tests/run.rs index ce21c903..25ab7018 100644 --- a/crates/prek/tests/run.rs +++ b/crates/prek/tests/run.rs @@ -2172,7 +2172,6 @@ fn selectors_completion() -> Result<()> { install-hooks Create environments for all hooks used in the config file run Run hooks list List hooks configured in the current workspace - list-builtins List all built-in hooks bundled with prek uninstall Uninstall prek from git hooks validate-config Validate configuration files (prek.toml or .pre-commit-config.yaml) validate-manifest Validate `.pre-commit-hooks.yaml` files diff --git a/docs/cli.md b/docs/cli.md index 6ec58578..f48b3626 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -16,7 +16,6 @@ prek [OPTIONS] [HOOK|PROJECT]... [COMMAND]
prek install-hooks

Create environments for all hooks used in the config file

prek run

Run hooks

prek list

List hooks configured in the current workspace

-
prek list-builtins

List all built-in hooks bundled with prek

prek uninstall

Uninstall prek from git hooks

prek validate-config

Validate configuration files (prek.toml or .pre-commit-config.yaml)

prek validate-manifest

Validate .pre-commit-hooks.yaml files

@@ -370,42 +369,6 @@ prek list [OPTIONS] [HOOK|PROJECT]...
--version, -V

Display the prek version

-## prek list-builtins - -List all built-in hooks bundled with prek - -

Usage

- -``` -prek list-builtins [OPTIONS] -``` - -

Options

- -
--cd, -C dir

Change to directory before running

-
--color color

Whether to use color in output

-

May also be set with the PREK_COLOR environment variable.

[default: auto]

Possible values:

-
    -
  • auto: Enables colored output only when the output is going to a terminal or TTY with support
  • -
  • always: Enables colored output regardless of the detected environment
  • -
  • never: Disables colored output
  • -
--config, -c config

Path to alternate config file

-
--help, -h

Display the concise help for this command

-
--log-file log-file

Write trace logs to the specified file. If not specified, trace logs will be written to $PREK_HOME/prek.log

-
--no-progress

Hide all progress outputs.

-

For example, spinners or progress bars.

-
--output-format output-format

The output format

-

[default: text]

Possible values:

-
    -
  • text
  • -
  • json
  • -
--quiet, -q

Use quiet output.

-

Repeating this option, e.g., -qq, will enable a silent mode in which prek will write no output to stdout.

-

May also be set with the PREK_QUIET environment variable.

--refresh

Refresh all cached data

-
--verbose, -v

Use verbose output

-
--version, -V

Display the prek version

-
- ## prek uninstall Uninstall prek from git hooks @@ -847,6 +810,7 @@ prek util [OPTIONS]

Commands

prek util identify

Show file identification tags

+
prek util list-builtins

List all built-in hooks bundled with prek

prek util init-template-dir

Install hook script in a directory intended for use with git config init.templateDir

prek util yaml-to-toml

Convert a YAML configuration file to prek.toml

@@ -892,6 +856,42 @@ prek util identify [OPTIONS] [PATH]...
--version, -V

Display the prek version

+### prek util list-builtins + +List all built-in hooks bundled with prek + +

Usage

+ +``` +prek util list-builtins [OPTIONS] +``` + +

Options

+ +
--cd, -C dir

Change to directory before running

+
--color color

Whether to use color in output

+

May also be set with the PREK_COLOR environment variable.

[default: auto]

Possible values:

+
    +
  • auto: Enables colored output only when the output is going to a terminal or TTY with support
  • +
  • always: Enables colored output regardless of the detected environment
  • +
  • never: Disables colored output
  • +
--config, -c config

Path to alternate config file

+
--help, -h

Display the concise help for this command

+
--log-file log-file

Write trace logs to the specified file. If not specified, trace logs will be written to $PREK_HOME/prek.log

+
--no-progress

Hide all progress outputs.

+

For example, spinners or progress bars.

+
--output-format output-format

The output format

+

[default: text]

Possible values:

+
    +
  • text
  • +
  • json
  • +
--quiet, -q

Use quiet output.

+

Repeating this option, e.g., -qq, will enable a silent mode in which prek will write no output to stdout.

+

May also be set with the PREK_QUIET environment variable.

--refresh

Refresh all cached data

+
--verbose, -v

Use verbose output

+
--version, -V

Display the prek version

+
+ ### prek util init-template-dir Install hook script in a directory intended for use with `git config init.templateDir`