Skip to the cargo

The standing crew: built-in linters ​

Builtins be the standing crew who come with the ship: reusable Pkl step definitions for linters, formatters, and hk's own utilities. Between them they bring file patterns, check and fix commands, and optimizations such as diff output.

Bring the tools aboard separately. A builtin calls on executables from yer environment; it does not install them. Provision them with yer project's package manager or with mise, the quartermaster.

Muster a builtin ​

pkl
amends "package://github.com/jdx/hk/releases/download/v2.5.0/hk@2.5.0#/Config.pkl"
import "package://github.com/jdx/hk/releases/download/v2.5.0/hk@2.5.0#/Builtins.pkl"

hooks {
  ["check"] {
    steps {
      ["prettier"] = Builtins.prettier
      ["eslint"] = Builtins.eslint
    }
  }
}

Builtins.prettier is the Pkl property name, the name the hand goes by on the roster. The step name, "prettier", is yer own label for that hand, the one ye name in calls such as hk check --step prettier.

Aye, and keep the schema and Builtins imports on the same version. The roster below describes the version of the source this website was built from; an older pinned package may differ.

Refit a builtin ​

Amend a builtin to keep its defaults while ye change particular properties:

pkl
["prettier"] = (Builtins.prettier) {
  glob = List("*.js", "*.ts", "*.json")
  exclude = List("**/generated/**")
  batch = false
}

Mind this: a property assignment replaces that property whole. If ye override glob or exclude, include every pattern ye want to keep.

Use dependencies to set the order the hands work in, and profiles (the ship's watches) for optional checks:

pkl
["prettier"] = (Builtins.prettier) {
  depends = "eslint"
}
["mypy"] = (Builtins.mypy) {
  profiles = List("types")
}

Run the type checker by calling its watch: hk check --profile types. See the configuration charts for groups, workspaces, and command templates.

Utilities hk carries aboard ​

Hands such as Builtins.trailing_whitespace, Builtins.newlines, and Builtins.check_merge_conflict call on hk util commands. These sail with hk itself and need no separate linter executable.

Tools on hand ​

A builtin charts how hk calls a tool; it does not install that tool. The executable the builtin uses must be on PATH, or the step must use a prefix that resolves it.

With the standing order HK_MISE=1, hk resolves the quartermaster's mise environment for the step's own directory, so tools declared in a subproject's mise.toml are on hand without a prefix. For a Node tool installed locally by aube, prefix its builtin with aube exec:

pkl
["eslint"] = (Builtins.eslint) {
  prefix = List("aube", "exec")
}

Mind ye, use an argv list for builtins backed by structured commands. A string prefix such as "aube exec" or "mise x --" cannot be combined with those commands.

The crew roster ​

The roster below is generated from the builtin definitions themselves, so it stays in plain English. Each entry gives the exact Pkl property to call the hand by. Look to the source definitions for more options and tests.

.NET ​

dotnet-format ​

Pkl: Builtins.dotnet_format

Format C# and Visual Basic with the .NET SDK

  • Files: **/*.cs, **/*.vb
  • Check: dotnet format --verify-no-changes --include {{files}}
  • Fix: dotnet format --include {{files}}
  • Options: exclusive = true

AsciiDoc ​

asciidoctor ​

Pkl: Builtins.asciidoctor

AsciiDoc syntax validator and processor

  • Files: asciidoc files
  • Check: asciidoctor --failure-level=WARNING --out-file=/dev/null {{files}}

Build Systems ​

buildifier-format ​

Pkl: Builtins.buildifier_format

Formatter for Bazel BUILD, WORKSPACE, and Starlark files

  • Files: **/BUILD, **/BUILD.bazel, WORKSPACE, WORKSPACE.bazel, MODULE.bazel, **/*.bzl, **/*.star, **/*.sky
  • Check: buildifier -mode=check {{files}}
  • Check (diff): hk util format-diff {{files}} -- buildifier '-path={}'
  • Fix: buildifier {{files}}

buildifier-lint ​

Pkl: Builtins.buildifier_lint

Linter for Bazel BUILD, WORKSPACE, and Starlark files

  • Files: **/BUILD, **/BUILD.bazel, WORKSPACE, WORKSPACE.bazel, MODULE.bazel, **/*.bzl, **/*.star, **/*.sky
  • Check: buildifier -mode=check -lint=warn {{files}}
  • Fix: buildifier -lint=fix {{files}}

CSS ​

stylelint ​

Pkl: Builtins.stylelint

CSS linter

  • Files: **/*.css, **/*.scss, **/*.sass, **/*.less
  • Check: stylelint {{files}}
  • Fix: stylelint --fix {{files}}

Configuration ​

sort-package-json ​

Pkl: Builtins.sort_package_json

Sort package.json keys

  • Files: **/package.json
  • Check: sort-package-json --check {{files}}
  • Fix: sort-package-json {{files}}

Data Formats ​

buf-format ​

Pkl: Builtins.buf_format

Protocol Buffers formatter

  • Files: **/*.proto
  • Check (diff): buf format {{workspace}} --diff --exit-code
  • Fix: buf format {{workspace}} --write
  • Options: workspace_indicator = "buf.yaml"

buf-lint ​

Pkl: Builtins.buf_lint

Protocol Buffers linter

  • Files: **/*.proto
  • Check: buf lint {{workspace}}
  • Options: workspace_indicator = "buf.yaml"

jq ​

Pkl: Builtins.jq

JSON processor

  • Files: **/*.json

  • Check (diff):

    sh
    failed=0
    broken=0
    trap 'rm -f "$out" "$patch" "$new"' EXIT
    trap 'exit 2' HUP INT TERM
    out=$(mktemp) && patch=$(mktemp) && new=$(mktemp) || exit 2
    n=0
    set --
    for f in {{ files }}; do
    n=$((n + 1))
    set -- "$@" --rawfile "f$n" "$f"
    done
    # Each file's output follows \u001e, which formatted JSON never
    # contains, and is \u001f if jq cannot parse the file.
    todo=all
    if [ "$n" -gt 1 ] && jq -n -j -S --argjson n "$n" "$@" '
    range(1; $n + 1) as $i | "\u001e", ($ARGS.named["f\($i)"]
    | try (fromjson | if type == "string" then tojson else . end, "\n") catch "\u001f")
    ' > "$new" 2>/dev/null; then
    todo=$(jq -n -r --argjson n "$n" --rawfile new "$new" "$@" '
    ($new | split("\u001e")) as $new
    | [range(1; $n + 1) | select($new[.] != $ARGS.named["f\(.)"]
    or ($new[.] | contains("\u001f") or contains("\ufffd"))) | tostring]
    | " " + join(" ") + " "
    ' 2>/dev/null) || todo=all
    fi
    i=0
    for f in {{ files }}; do
    i=$((i + 1))
    case $todo in
    all | *" $i "*) ;;
    *) continue ;;
    esac
    if jq -S . "$f" > "$out"; then
    # hk runs this with `set -e`, so take diff's status through `||`.
    status=0
    diff -u -L "a/$f" -L "b/$f" "$f" "$out" >> "$patch" || status=$?
    case $status in
    0) ;;
    1) failed=1 ;;
    *) broken=1 ;;
    esac
    else
    broken=1
    fi
    done
    # When a file failed to parse or diff, print no patch: hk then runs the
    # fixer on every file, and the fixer reports the error.
    [ "$broken" = 1 ] && exit 1
    cat "$patch" || exit 2
    exit $failed
  • Fix:

    sh
    failed=0
    tmp=
    trap 'rm -f "$tmp"' EXIT
    trap 'exit 2' HUP INT TERM
    for f in {{ files }}; do
    # Write a copy beside the file, with its permissions, then rename it
    # over the file, so a failed write never truncates the original. A
    # file that is already formatted is left alone, links and all.
    tmp=$(mktemp "$f.XXXXXX") &&
    cp -p "$f" "$tmp" &&
    jq -S . "$f" > "$tmp" &&
    { cmp -s "$f" "$tmp" || mv "$tmp" "$f"; } || failed=1
    rm -f "$tmp"
    tmp=
    done
    exit $failed
  • Options: batch = true

sql-fluff ​

Pkl: Builtins.sql_fluff

SQL linter and formatter

  • Files: **/*.sql
  • Check: sqlfluff lint {{files}}
  • Fix: sqlfluff fix {{files}}

taplo ​

Pkl: Builtins.taplo

TOML linter

  • Files: **/*.toml
  • Check: taplo lint {{files}}

taplo-format ​

Pkl: Builtins.taplo_format

TOML formatter

  • Files: **/*.toml

  • Check: taplo format --check --diff {{files}}

  • Check (diff):

    sh
    # Windows: nothing runs
    # other platforms:
    set --
    for f in {{ files }}; do
    taplo format --check --colors never "$f" > /dev/null 2>&1 || set -- "$@" "$f"
    done
    [ $# -eq 0 ] && exit 0
    hk util format-diff "$@" -- taplo format --colors never --stdin-filepath {} -
  • Fix: taplo format {{files}}

tombi ​

Pkl: Builtins.tombi

TOML linter

  • Files: **/*.toml
  • Check: tombi lint --quiet {{files}}

tombi-format ​

Pkl: Builtins.tombi_format

TOML formatter

  • Files: **/*.toml
  • Check: tombi format --quiet --check --diff {{files}}
  • Check (diff): hk util format-diff {{files}} -- tombi format --quiet --stdin-filename '{}' -
  • Fix: tombi format --quiet {{files}}

xmllint ​

Pkl: Builtins.xmllint

XML validator

  • Files: **/*.xml
  • Check: xmllint --noout {{files}}

yamlfmt ​

Pkl: Builtins.yamlfmt

YAML formatter

  • Files: **/*.yml, **/*.yaml

  • Check: yamlfmt -lint {{files}}

  • Check (diff):

    sh
    # Windows: nothing runs
    # other platforms:
    # If yamlfmt fails, print no patch: hk runs the fixer, which reports it.
    list=$(yamlfmt -dry -quiet {{ files }}) || exit $?
    set --
    # After a heading, `-dry -quiet` lists the files that would change.
    # Anything else it lists, such as part of a name containing a newline,
    # prints no patch, so hk runs the fixer instead of skipping the file.
    # The here-document expands `$list` once; the names in it are not
    # expanded again, so `$(...)` in a name is just text.
    while IFS= read -r f; do
    case $f in
    '' | 'The following files would be formatted:') continue ;;
    esac
    if [ ! -f "$f" ]; then
    echo "yamlfmt listed a file that does not exist: $f" >&2
    exit 1
    fi
    set -- "$@" "$f"
    done <<EOF
    $list
    EOF
    [ $# -eq 0 ] && exit 0
    hk util format-diff "$@" -- yamlfmt -in
  • Fix: yamlfmt {{files}}

yamllint ​

Pkl: Builtins.yamllint

YAML linter

  • Files: **/*.yml, **/*.yaml
  • Check: yamllint --strict {{files}}

yq ​

Pkl: Builtins.yq

YAML processor

  • Files: **/*.yaml, **/*.yml

  • Check (diff):

    sh
    failed=0
    broken=0
    trap 'rm -rf "$out" "$patch" "$split"' EXIT
    trap 'exit 2' HUP INT TERM
    out=$(mktemp) && patch=$(mktemp) && split=$(mktemp -d) || exit 2
    set --
    for f in {{ files }}; do
    case $f in
    *.[yY][aA][mM][lL] | *.[yY][mM][lL]) set -- "$@" "$f" ;;
    esac
    done
    fast=0
    if [ $# -gt 1 ] && YQ_SPLIT="$split/" yq -P -N -s 'strenv(YQ_SPLIT) +
    (file_index | tostring) + "-" + (document_index | tostring)' "$@" > /dev/null 2>&1; then
    fast=1
    fi
    i=0
    for f in {{ files }}; do
    new=
    case $f in
    *.[yY][aA][mM][lL] | *.[yY][mM][lL])
    new="$split/$i-0.yml"
    i=$((i + 1))
    ;;
    esac
    if [ "$fast" = 1 ] && [ -n "$new" ] && [ -f "$new" ] &&
    [ ! -e "${new%-0.yml}-1.yml" ] && cmp -s "$f" "$new"; then
    continue
    fi
    if yq -P "$f" > "$out"; then
    # hk runs this with `set -e`, so take diff's status through `||`.
    status=0
    diff -u -L "a/$f" -L "b/$f" "$f" "$out" >> "$patch" || status=$?
    case $status in
    0) ;;
    1) failed=1 ;;
    *) broken=1 ;;
    esac
    else
    broken=1
    fi
    done
    # When a file failed to parse or diff, print no patch: hk then runs the
    # fixer on every file, and the fixer reports the error.
    [ "$broken" = 1 ] && exit 1
    cat "$patch" || exit 2
    exit $failed
  • Fix:

    sh
    failed=0
    for f in {{ files }}; do
    yq -iP "$f" || failed=1
    done
    exit $failed
  • Options: batch = true

EditorConfig ​

editorconfig-checker ​

Pkl: Builtins.editorconfig_checker

Validate files against EditorConfig rules

  • Files: text files
  • Check: editorconfig-checker {{files}}
  • Fix: editorconfig-checker --fix {{files}}

Elixir ​

mix-compile ​

Pkl: Builtins.mix_compile

Compile Elixir with Mix

  • Files: **/*.ex
  • Check: mix compile --warnings-as-errors --strict-errors {{files}}
  • Options: exclude = List("deps/**/*")

mix-fmt ​

Pkl: Builtins.mix_fmt

Format Elixir with Mix

  • Files: **/*.ex, **/*.exs
  • Check: mix format --check-formatted {{files}}
  • Fix: mix format {{files}}
  • Options: exclude = List("deps/**/*")

mix-test ​

Pkl: Builtins.mix_test

Test Elixir with Mix

  • Files: **/*.ex, **/*.exs
  • Check: mix test --warnings-as-errors {{files}}
  • Options: exclude = List("deps/**/*")

Go ​

err-check ​

Pkl: Builtins.err_check

Error handling checker

  • Files: **/*.go
  • Check: errcheck ./...
  • Options: workspace_indicator = "go.mod", dir = "{{workspace}}"

go-fix ​

Pkl: Builtins.go_fix

Go modernizer (requires Go 1.26+)

  • Files: **/*.go
  • Check (diff): go fix -diff ./...
  • Fix: go fix ./...
  • Options: workspace_indicator = "go.mod", dir = "{{workspace}}"

go-fmt ​

Pkl: Builtins.go_fmt

Go formatter

  • Files: **/*.go

  • Check (diff):

    sh
    DIFF=$(gofmt -s -d {{files}})
    if [ -n "$DIFF" ]; then
    echo "$DIFF"
    exit 1
    fi
  • Check (list files):

    sh
    FILES=$(gofmt -s -l {{files}})
    if [ -n "$FILES" ]; then
    echo "$FILES"
    exit 1
    fi
  • Fix: gofmt -s -w {{files}}

go-fumpt ​

Pkl: Builtins.go_fumpt

Strict Go formatter

  • Files: **/*.go

  • Check (diff): gofumpt -d {{files}}

  • Check (list files):

    sh
    FILES=$(gofumpt -l {{files}})
    if [ -n "$FILES" ]; then
    echo "$FILES"
    exit 1
    fi
  • Fix: gofumpt -w {{files}}

go-imports ​

Pkl: Builtins.go_imports

Go import management

  • Files: **/*.go

  • Check (diff):

    sh
    DIFF=$(goimports -d {{files}})
    if [ -n "$DIFF" ]; then
    echo "$DIFF"
    exit 1
    fi
  • Check (list files):

    sh
    FILES=$(goimports -l {{files}})
    if [ -n "$FILES" ]; then
    echo "$FILES"
    exit 1
    fi
  • Fix: goimports -w {{files}}

go-lines ​

Pkl: Builtins.go_lines

Long line fixer

  • Files: **/*.go
  • Check (diff): hk util format-diff {{files}} -- golines
  • Fix: golines -w {{files}}

go-sec ​

Pkl: Builtins.go_sec

Security scanner

  • Files: **/*.go
  • Check: gosec ./...
  • Options: workspace_indicator = "go.mod", dir = "{{workspace}}"

go-vet ​

Pkl: Builtins.go_vet

Go code vetting

  • Files: **/*.go
  • Check: go vet ./...
  • Options: workspace_indicator = "go.mod", dir = "{{workspace}}"

go-vuln-check ​

Pkl: Builtins.go_vuln_check

Vulnerability scanner

  • Files: **/*.go
  • Check: govulncheck ./...
  • Options: workspace_indicator = "go.mod", dir = "{{workspace}}"

golangci-lint ​

Pkl: Builtins.golangci_lint

Go meta-linter

  • Files: **/*.go
  • Check: golangci-lint run --fix=false ./...
  • Fix: golangci-lint run --fix ./...
  • Options: workspace_indicator = "go.mod", dir = "{{workspace}}"

golangci-lint-fmt ​

Pkl: Builtins.golangci_lint_fmt

Go formatter (part of golangci-lint)

  • Files: **/*.go
  • Check (diff): golangci-lint fmt --diff {{files}}
  • Fix: golangci-lint fmt {{files}}

gomod-tidy ​

Pkl: Builtins.gomod_tidy

Go module maintenance

  • Files: **/*.go, **/go.mod, **/go.sum
  • Check (diff): go mod tidy -diff
  • Fix: go mod tidy
  • Options: workspace_indicator = "go.mod", dir = "{{workspace}}", stage = List("go.mod", "go.sum")

revive ​

Pkl: Builtins.revive

Fast Go linter

  • Files: **/*.go
  • Check: revive -set_exit_status ./...
  • Options: workspace_indicator = "go.mod", dir = "{{workspace}}"

staticcheck ​

Pkl: Builtins.staticcheck

Go static analysis

  • Files: **/*.go
  • Check: staticcheck ./...
  • Options: workspace_indicator = "go.mod", dir = "{{workspace}}"

Infrastructure ​

actionlint ​

Pkl: Builtins.actionlint

GitHub Actions workflow linter

  • Files: .github/workflows/*.yml, .github/workflows/*.yaml
  • Check: actionlint {{files}}
  • Options: batch = true

dclint ​

Pkl: Builtins.dclint

Docker Compose linter

  • Files: **/compose*.yml, **/compose*.yaml, **/docker-compose*.yml, **/docker-compose*.yaml
  • Check: dclint {{files}}
  • Fix: dclint --fix {{files}}
  • Options: batch = true

droast ​

Pkl: Builtins.droast

Dockerfile linter for security, layer hygiene, and best-practice violations

  • Files: **/Dockerfile, **/Dockerfile.*, **/*.Dockerfile, **/*.dockerfile, **/Containerfile, **/Containerfile.*
  • Check: droast {{files}}
  • Fix: droast --fix {{files}}

ghalint-action ​

Pkl: Builtins.ghalint_action

GitHub Actions action linter

  • Files: **/action.* that are yaml files
  • Check: ghalint run-action {{files}}

ghalint-workflow ​

Pkl: Builtins.ghalint_workflow

GitHub Actions workflow linter

  • Files: .github/workflows/* that are yaml files
  • Check: ghalint run

hadolint ​

Pkl: Builtins.hadolint

Dockerfile linter

  • Files: **/Dockerfile*
  • Check: hadolint {{files}}

hclfmt ​

Pkl: Builtins.hclfmt

Hashicorp's hclfmt

  • Files: **/*.hcl
  • Fix: hclfmt -w {{files}}

jactionlint ​

Pkl: Builtins.jactionlint

GitHub Actions workflow linter (jdx/jactionlint, a maintained actionlint fork)

  • Files: .github/workflows/*.yml, .github/workflows/*.yaml
  • Check: jactionlint {{files}}
  • Options: batch = true

kube-linter ​

Pkl: Builtins.kube_linter

Check Kubernetes manifests for security and reliability issues

  • Files: **/k8s/**/*.yaml, **/k8s/**/*.yml, **/kubernetes/**/*.yaml, **/kubernetes/**/*.yml, **/manifests/**/*.yaml, **/manifests/**/*.yml, **/deploy/**/*.yaml, **/deploy/**/*.yml, **/deployments/**/*.yaml, **/deployments/**/*.yml
  • Check: kube-linter lint --fail-if-no-objects-found {{files}}
  • Options: exclude = List("**/templates/**", "**/values.yaml", "**/values.yml", "**/values-*.yaml", "**/values-*.yml", "**/kustomization.yaml", "**/kustomization.yml")

kubeconform ​

Pkl: Builtins.kubeconform

Kubernetes manifest validator

  • Files: **/k8s/**/*.yaml, **/k8s/**/*.yml, **/kubernetes/**/*.yaml, **/kubernetes/**/*.yml, **/manifests/**/*.yaml, **/manifests/**/*.yml, **/deploy/**/*.yaml, **/deploy/**/*.yml, **/deployments/**/*.yaml, **/deployments/**/*.yml
  • Check: kubeconform -summary -strict -ignore-missing-schemas {{files}}
  • Options: exclude = List("**/templates/**", "**/values.yaml", "**/values.yml", "**/kustomization.yaml", "**/kustomization.yml")

mise ​

Pkl: Builtins.mise

mise-en-place's built-in formatter and linter

  • Files: mise.toml, mise.*.toml, .mise.toml, .mise.*.toml, mise/config.toml, mise/config.*.toml, .mise/config.toml, .mise/config.*.toml, .config/mise.toml, .config/mise.*.toml, .config/mise/config.toml, .config/mise/config.*.toml, .config/mise/mise.toml, .config/mise/mise.*.toml, .config/mise/conf.d/*.toml
  • Check: mise fmt --check
  • Fix: mise fmt

pinact ​

Pkl: Builtins.pinact

Pin GitHub Actions to commit SHA for security

  • Files: .github/workflows/*, **/action.* that are yaml files
  • Check: pinact run --verify-comment --check {{files}}
  • Check (diff): hk util sarif-diff -- pinact run --verify-comment --check --format sarif {{files}}
  • Fix: pinact run --verify-comment {{files}}

pinact-update ​

Pkl: Builtins.pinact_update

Pin GitHub Actions to update actions to latest versions

  • Files: .github/workflows/*, **/action.* that are yaml files
  • Check: pinact run --update --verify-comment --check {{files}}
  • Check (diff): hk util sarif-diff -- pinact run --update --verify-comment --check --format sarif {{files}}
  • Fix: pinact run --update --verify-comment {{files}}

terraform ​

Pkl: Builtins.terraform

Terraform formatter

  • Files: **/*.tf, **/*.tfvars, **/*.tftest.hcl
  • Check: terraform fmt -check {{files}}
  • Check (diff): hk util format-diff {{files}} -- terraform fmt -
  • Fix: terraform fmt {{files}}

terraform-docs ​

Pkl: Builtins.terraform_docs

Terraform module documentation generator

  • Files: **/*.tf, **/*.tofu, **/.terraform.lock.hcl

  • Check:

    sh
    for f in {{ files }}; do dirname "$f"; done | sort -u | { code=0; while IFS= read -r dir; do
    terraform-docs markdown table --output-check --output-file README.md --output-mode inject "$dir" || code=1
    done; exit $code; }
  • Fix:

    sh
    for f in {{ files }}; do dirname "$f"; done | sort -u | { code=0; while IFS= read -r dir; do
    terraform-docs markdown table --output-file README.md --output-mode inject "$dir" || code=1
    done; exit $code; }
  • Options: exclude = "**/.terraform/**"

terraform-validate ​

Pkl: Builtins.terraform_validate

Terraform configuration validator

  • Files: **/*.tf, **/*.tfvars, **/*.tf.json, **/*.tofu, **/.terraform.lock.hcl

  • Check:

    sh
    for f in {{ files }}; do dirname "$f"; done | sort -u | { code=0; while IFS= read -r dir; do
    terraform -chdir="$dir" validate && continue
    terraform -chdir="$dir" init -backend=false -input=false > /dev/null || { code=1; continue; }
    terraform -chdir="$dir" validate || code=1
    done; exit $code; }
  • Options: exclude = "**/.terraform/**"

terragrunt-hcl-fmt ​

Pkl: Builtins.terragrunt_hcl_fmt

Terragrunt HCL formatter

  • Files: **/*.hcl

  • Check:

    sh
    set --
    for f in {{ files }}; do set -- "$@" "--filter=./$f"; done
    if [ "$#" -eq 0 ]; then exit 0; fi
    terragrunt hcl fmt --check --diff "$@"
  • Fix:

    sh
    set --
    for f in {{ files }}; do set -- "$@" "--filter=./$f"; done
    if [ "$#" -eq 0 ]; then exit 0; fi
    terragrunt hcl fmt "$@"
  • Options: exclude = List("**/.terraform/**", "**/.terragrunt-cache/**")

terragrunt-hcl-validate ​

Pkl: Builtins.terragrunt_hcl_validate

Terragrunt HCL validator

  • Files: **/*.hcl

  • Check:

    sh
    set --
    for f in {{ files }}; do
    case "$f" in */*) d="./${f%/*}" ;; *) d="." ;; esac
    set -- "$@" "--filter=$d" "--filter=reading=./$f"
    done
    if [ "$#" -eq 0 ]; then exit 0; fi
    terragrunt hcl validate --show-config-path --log-level error "$@"
  • Options: exclude = List("**/.terraform/**", "**/.terragrunt-cache/**")

tf-lint ​

Pkl: Builtins.tf_lint

Terraform linter

  • Files: **/*.tf, **/*.tfvars, **/*.tofu

  • Check:

    sh
    for f in {{ files }}; do dirname "$f"; done | sort -u | { code=0; while IFS= read -r dir; do
    tflint --chdir="$dir" || code=1
    done; exit $code; }
  • Fix:

    sh
    for f in {{ files }}; do dirname "$f"; done | sort -u | { code=0; while IFS= read -r dir; do
    tflint --chdir="$dir" --fix || tflint --chdir="$dir" || code=1
    done; exit $code; }
  • Options: exclude = "**/.terraform/**"

tofu ​

Pkl: Builtins.tofu

OpenTofu formatter

  • Files: **/*.tf, **/*.tfvars, **/*.tftest.hcl
  • Check: tofu fmt -check {{files}}
  • Check (diff): hk util format-diff {{files}} -- tofu fmt -
  • Fix: tofu fmt {{files}}

zizmor ​

Pkl: Builtins.zizmor

GitHub Actions security static analysis tool

  • Files: .github/workflows/*.yml, .github/workflows/*.yaml, .github/dependabot.yml, **/action.yml, **/action.yaml
  • Check: zizmor --no-progress {{files}}
  • Fix: zizmor --no-progress --fix {{files}}

Java ​

google-java-format ​

Pkl: Builtins.google_java_format

Google Java Format

  • Files: **/*.java
  • Check: google-java-format --dry-run --set-exit-if-changed {{files}}
  • Check (list files): google-java-format --dry-run {{files}}
  • Fix: google-java-format --replace {{files}}

JavaScript/TypeScript ​

biome ​

Pkl: Builtins.biome

Fast formatter and linter

  • Files: **/*.js, **/*.jsx, **/*.ts, **/*.tsx, **/*.json
  • Check: biome check --no-errors-on-unmatched {{files}}
  • Fix: biome check --write --no-errors-on-unmatched {{files}}

deno ​

Pkl: Builtins.deno

Deno formatter

  • Files: **/*.js, **/*.jsx, **/*.ts, **/*.tsx
  • Check: deno fmt --check {{files}}
  • Fix: deno fmt {{files}}

deno-check ​

Pkl: Builtins.deno_check

Deno type checker

  • Files: **/*.js, **/*.jsx, **/*.ts, **/*.tsx
  • Check: deno check {{files}}

eslint ​

Pkl: Builtins.eslint

Pluggable JavaScript linter

  • Files: **/*.js, **/*.jsx, **/*.ts, **/*.tsx
  • Check: eslint {{files}}
  • Fix: eslint --fix {{files}}
  • Options: batch = true

knip ​

Pkl: Builtins.knip

Find unused files, dependencies, and exports

  • Files: javascript, typescript files
  • Check: knip --no-progress --directory {{workspace}}
  • Fix: knip --fix --no-progress --directory {{workspace}}
  • Options: workspace_indicator = "package.json"

ox-lint ​

Pkl: Builtins.ox_lint

Oxidation compiler linter

  • Files: **/*.js, **/*.mjs, **/*.cjs, **/*.ts, **/*.mts, **/*.cts, **/*.jsx, **/*.tsx, **/*.vue, **/*.svelte, **/*.astro
  • Check: oxlint --deny-warnings {{files}}
  • Fix: oxlint --deny-warnings --fix {{files}}

oxfmt ​

Pkl: Builtins.oxfmt

Fast Rust-powered code formatter (Prettier compatible)

  • Files: **/*.js, **/*.mjs, **/*.cjs, **/*.ts, **/*.mts, **/*.cts, **/*.jsx, **/*.tsx, **/*.mdx, **/*.vue, **/*.json, **/*.jsonc, **/*.json5, **/*.yaml, **/*.yml, **/*.toml, **/*.md, **/*.markdown, **/*.html, **/*.htm, **/*.css, **/*.scss, **/*.less, **/*.graphql, **/*.gql, **/*.handlebars, **/*.hbs
  • Check: oxfmt --check --no-error-on-unmatched-pattern {{files}}
  • Check (diff): hk util format-diff {{files}} -- oxfmt --stdin-filepath '{}'
  • Fix: oxfmt --write --no-error-on-unmatched-pattern {{files}}

prettier ​

Pkl: Builtins.prettier

Opinionated code formatter

  • Files: **/*.js, **/*.jsx, **/*.mjs, **/*.cjs, **/*.ts, **/*.tsx, **/*.mts, **/*.cts, **/*.css, **/*.scss, **/*.less, **/*.html, **/*.json, **/*.json5, **/*.jsonc, **/*.yaml, **/*.yml, **/*.markdown, **/*.markdown.mdx, **/*.md, **/*.graphql, **/*.handlebars, **/*.svelte, **/*.astro, **/*.htmlangular, **/*.vue
  • Check: prettier --check {{files}}
  • Check (list files): prettier --list-different {{files}}
  • Fix: prettier --write {{files}}
  • Options: batch = true

sherif ​

Pkl: Builtins.sherif

Opinionated, zero-config linter for TypeScript & JavaScript monorepos

  • Files: **/package.json, **/pnpm-workspace.yaml
  • Check: sherif
  • Fix: sherif --fix --no-install

standard-js ​

Pkl: Builtins.standard_js

JavaScript Standard Style

  • Files: **/*.js, **/*.jsx, **/*.ts, **/*.tsx
  • Check: standard {{files}}
  • Fix: standard --fix {{files}}

tsc ​

Pkl: Builtins.tsc

TypeScript type checker

  • Files: **/*.ts, **/*.tsx
  • Check: tsc --noEmit -p {{workspace_indicator}}
  • Options: workspace_indicator = "tsconfig.json"

tsserver ​

Pkl: Builtins.tsserver

TypeScript language server diagnostics

  • Files: **/*.ts, **/*.tsx
  • Check: tsc-files --noEmit {{files}}

vp-check ​

Pkl: Builtins.vp_check

Formats, lints, and type checks code together

  • Files: **/*.js, **/*.mjs, **/*.cjs, **/*.ts, **/*.mts, **/*.cts, **/*.jsx, **/*.tsx, **/*.mdx, **/*.vue, **/*.svelte, **/*.astro, **/*.json, **/*.jsonc, **/*.json5, **/*.yaml, **/*.yml, **/*.md, **/*.markdown, **/*.html, **/*.htm, **/*.css, **/*.scss, **/*.less, **/*.graphql, **/*.gql, **/*.handlebars, **/*.hbs, **/*.toml
  • Check: vp check {{files}}
  • Fix: vp check --fix {{files}}
  • Options: workspace_indicator = "package.json"

vp-fmt ​

Pkl: Builtins.vp_fmt

Formats code with Oxfmt

  • Files: **/*.js, **/*.mjs, **/*.cjs, **/*.ts, **/*.mts, **/*.cts, **/*.jsx, **/*.tsx, **/*.mdx, **/*.vue, **/*.json, **/*.jsonc, **/*.json5, **/*.yaml, **/*.yml, **/*.toml, **/*.md, **/*.markdown, **/*.html, **/*.htm, **/*.css, **/*.scss, **/*.less, **/*.graphql, **/*.gql, **/*.handlebars, **/*.hbs
  • Check: vp fmt --check {{files}}
  • Check (list files): vp fmt --list-different {{files}}
  • Fix: vp fmt {{files}}
  • Options: workspace_indicator = "package.json"

vp-lint ​

Pkl: Builtins.vp_lint

Lints code with OxLint

  • Files: **/*.js, **/*.mjs, **/*.cjs, **/*.ts, **/*.mts, **/*.cts, **/*.jsx, **/*.tsx, **/*.mdx, **/*.vue, **/*.svelte, **/*.astro
  • Check: vp lint --deny-warnings {{files}}
  • Fix: vp lint --deny-warnings --fix {{files}}
  • Options: workspace_indicator = "package.json"

xo ​

Pkl: Builtins.xo

JavaScript/TypeScript linter with great defaults

  • Files: **/*.js, **/*.jsx, **/*.ts, **/*.tsx
  • Check: xo {{files}}
  • Fix: xo --fix {{files}}

Lua ​

luacheck ​

Pkl: Builtins.luacheck

Lua linter

  • Files: lua files
  • Check: luacheck {{files}}

selene ​

Pkl: Builtins.selene

Lua linter

  • Files: lua files
  • Check: selene {{files}}

stylua ​

Pkl: Builtins.stylua

Lua formatter

  • Files: lua files
  • Check: stylua --check {{files}}
  • Check (diff): hk util format-diff {{files}} -- stylua --stdin-filepath '{}' -
  • Fix: stylua {{files}}

Markdown ​

contextlint ​

Pkl: Builtins.contextlint

Rule-based linter for structured Markdown documents

  • Files: markdown files
  • Check: contextlint {{files}}
  • Options: workspace_indicator = "contextlint.config.json"

mado ​

Pkl: Builtins.mado

Fast Markdown linter (CommonMark/GFM)

  • Files: markdown files
  • Check: mado check {{files}}

markdown-lint ​

Pkl: Builtins.markdown_lint

Markdown linter

  • Files: **/*.md, **/*.markdown
  • Check: markdownlint {{files}}
  • Fix: markdownlint --fix {{files}}

mdschema ​

Pkl: Builtins.mdschema

Schema-based Markdown documentation validator

  • Files: markdown files
  • Check: mdschema check {{files}}
  • Options: workspace_indicator = ".mdschema.yml"

rumdl ​

Pkl: Builtins.rumdl

Markdown linter

  • Files: **/*.md, **/*.markdown
  • Check: rumdl check {{files}}
  • Check (diff): rumdl check --diff {{files}}
  • Fix: rumdl check --fix {{files}}
  • Options: check_after_diff = true

rumdl-format ​

Pkl: Builtins.rumdl_format

Markdown formatter

  • Files: **/*.md, **/*.markdown
  • Check (diff): rumdl fmt --check --diff {{files}}
  • Fix: rumdl fmt {{files}}

ryl-markdown ​

Pkl: Builtins.ryl_markdown

Lint YAML embedded in Markdown (front matter + fenced yaml blocks) with ryl

  • Files: markdown files
  • Check: ryl --markdown {{files}}
  • Check (diff): ryl --diff --markdown {{files}}
  • Check (list files): ryl --list-files --markdown {{files}}
  • Fix: ryl --fix --markdown {{files}}
  • Options: check_after_diff = true

Nix ​

alejandra ​

Pkl: Builtins.alejandra

Alternative Nix formatter

  • Files: **/*.nix
  • Check: alejandra --check {{files}}
  • Fix: alejandra {{files}}

deadnix ​

Pkl: Builtins.deadnix

Scan Nix files for dead code

  • Files: **/*.nix
  • Check: deadnix --fail {{files}}
  • Fix: deadnix --edit {{files}}

nil ​

Pkl: Builtins.nil

Nix Language server

  • Files: **/*.nix
  • Check: nil diagnostics --deny-warnings {{files}}

nix-fmt ​

Pkl: Builtins.nix_fmt

Nix formatter

  • Files: **/*.nix

  • Check:

    sh
    # Windows: nothing runs
    # other platforms:
    nixfmt --check {{ files }}
  • Fix:

    sh
    # Windows: nothing runs
    # other platforms:
    nixfmt {{ files }}

nixf-diagnose ​

Pkl: Builtins.nixf_diagnose

Nix linter based on libnixf

  • Files: **/*.nix
  • Check: nixf-diagnose {{files}}

nixpkgs-format ​

Pkl: Builtins.nixpkgs_format

Nixpkgs formatter

  • Files: **/*.nix
  • Check: nixpkgs-fmt --check {{files}}
  • Fix: nixpkgs-fmt {{files}}

Other Languages ​

astro ​

Pkl: Builtins.astro

Astro component checker

  • Files: **/*.astro
  • Check: astro check {{files}}

clang-format ​

Pkl: Builtins.clang_format

C/C++ formatter

  • Files: **/*.c, **/*.h, **/*.cpp, **/*.hpp, **/*.cc, **/*.hh, **/*.cxx, **/*.hxx
  • Check: clang-format --dry-run -Werror {{files}}
  • Fix: clang-format -i {{files}}

cmake-format ​

Pkl: Builtins.cmake_format

Source code formatter for cmake listfiles

  • Files: **/CMakeLists.txt, **/*.cmake
  • Check: cmake-format --check {{files}}
  • Fix: cmake-format --in-place {{files}}

cpp-lint ​

Pkl: Builtins.cpp_lint

C++ style checker

  • Files: **/*.c, **/*.h, **/*.cpp, **/*.hpp, **/*.cc, **/*.hh, **/*.cxx, **/*.hxx
  • Check: cpplint {{files}}

dprint ​

Pkl: Builtins.dprint

Pluggable code formatter

  • Files: text files
  • Check: dprint check --allow-no-files {{files}}
  • Check (list files): dprint check --allow-no-files --list-different {{files}}
  • Fix: dprint fmt --allow-no-files {{files}}

ktlint ​

Pkl: Builtins.ktlint

Kotlin linter and formatter

  • Files: **/*.kt
  • Check: ktlint {{files}}
  • Fix: ktlint -F {{files}}

swiftlint ​

Pkl: Builtins.swiftlint

Swift style and conventions

  • Files: **/*.swift
  • Check: swiftlint lint {{files}}
  • Fix: swiftlint --fix {{files}}

vacuum ​

Pkl: Builtins.vacuum

Fast OpenAPI linter

  • Files: **/*openapi*.yaml, **/*openapi*.yml, **/*openapi*.json, **/*swagger*.yaml, **/*swagger*.yml, **/*swagger*.json
  • Check: vacuum lint {{files}}
  • Fix: vacuum lint --fix {{files}}

PHP ​

php-cs ​

Pkl: Builtins.php_cs

PHP coding standards

  • Files: **/*.php
  • Check: phpcs {{files}}
  • Fix: phpcbf {{files}}

Package ​

aqua-update-checksum ​

Pkl: Builtins.aqua_update_checksum

create or update aqua checksums

  • Files: {aqua,.aqua}.{yml,yaml}, {aqua,.aqua}/**/*.{yml,yaml}, aqua-checksums.json, {aqua,.aqua}/aqua-checksums.json
  • Fix: aqua update-checksum --prune

Pkl ​

pkl ​

Pkl: Builtins.pkl

Pkl configuration language

  • Files: pkl files
  • Check: pkl eval {{files}} >/dev/null

pkl-format ​

Pkl: Builtins.pkl_format

Pkl formatter

  • Files: pkl files

  • Check (list files): pkl format --diff-name-only {{ files }}

  • Fix:

    sh
    pkl format --write {{ files }}
    code=$?
    if [ $code -eq 11 ]; then
    exit 0
    else
    exit $code
    fi
  • Options: shell = "sh -c"

Python ​

black ​

Pkl: Builtins.black

Opinionated Python formatter

  • Files: **/*.py

  • Check: black --check {{files}}

  • Check (list files):

    sh
    # Windows: nothing runs
    # other platforms:
    set -- {{ files }}
    out=$(black --check "$@" 2>&1) && exit 0
    status=$?
    printf '%s\n' "$out" | while IFS= read -r line; do
    case "$line" in
    "would reformat "*) printf '%s\n' "${line#would reformat }" ;;
    "error: cannot format "*)
      # The message after the path may contain ": ", so match the inputs.
      for f; do
      case "$line" in "error: cannot format $f: "*) printf '%s\n' "$f" ;; esac
      done
      ;;
    esac
    done
    exit $status
  • Fix: black {{files}}

flake8 ​

Pkl: Builtins.flake8

Python style guide enforcement

  • Files: **/*.py
  • Check: flake8 {{files}}

isort ​

Pkl: Builtins.isort

Python import sorter

  • Files: **/*.py
  • Check: isort --check-only {{files}}
  • Check (diff): isort --check-only --diff {{files}}
  • Fix: isort {{files}}

mypy ​

Pkl: Builtins.mypy

Static type checker for Python

  • Files: **/*.py, **/*.pyi
  • Check: mypy {{files}}

pylint ​

Pkl: Builtins.pylint

Python code analysis

  • Files: **/*.py
  • Check: pylint {{files}}

python-check-ast ​

Pkl: Builtins.python_check_ast

Validate Python syntax by parsing the AST

  • Files: **/*.py
  • Check: hk util python-check-ast {{files}}

python-debug-statements ​

Pkl: Builtins.python_debug_statements

Detect debug statements in Python code

  • Files: **/*.py
  • Check: hk util python-debug-statements {{files}}

ruff ​

Pkl: Builtins.ruff

Fast Python linter

  • Files: python files
  • Check: ruff check --force-exclude {{files}}
  • Check (diff): ruff check --force-exclude --diff --exit-non-zero-on-fix {{ files }} && ruff check --force-exclude {{ files }}
  • Fix: ruff check --force-exclude --fix {{files}}
  • Options: check_after_diff = true

ruff-format ​

Pkl: Builtins.ruff_format

Fast Python formatter (part of ruff)

  • Files: python files
  • Check (diff): ruff format --quiet --force-exclude --diff {{files}}
  • Fix: ruff format --quiet --force-exclude {{files}}

ty ​

Pkl: Builtins.ty

Fast Python type checker

  • Files: **/*.py, **/*.pyi
  • Check: ty check {{files}}

Ruby ​

brakeman ​

Pkl: Builtins.brakeman

Security scanner for Rails

  • Files: ruby files
  • Check: brakeman -q -w2 {{files}}

bundle-audit ​

Pkl: Builtins.bundle_audit

Dependency security audit

  • Files: **/Gemfile.lock
  • Check: bundle-audit check {{files}}
  • Fix: bundle-audit update

erb ​

Pkl: Builtins.erb

ERB template linter

  • Files: **/*.erb
  • Check: erb -P -x -T - {{ files }} | ruby -c

fasterer ​

Pkl: Builtins.fasterer

Performance suggestions

  • Files: ruby files
  • Check: fasterer {{files}}

reek ​

Pkl: Builtins.reek

Code smell detector

  • Files: ruby files
  • Check: reek {{files}}

rubocop ​

Pkl: Builtins.rubocop

Ruby style guide

  • Files: **/*.rb, **/*.arb, **/*.axlsx, **/*.builder, **/*.gemfile, **/*.gemspec, **/*.jb, **/*.jbuilder, **/*.mspec, **/*.opal, **/*.pluginspec, **/*.podspec, **/*.rabl, **/*.rake, **/*.rbw, **/*.ru, **/*.ruby, **/*.schema, **/*.spec, **/*.thor, **/.irbrc, **/.pryrc, **/.simplecov, **/buildfile, **/Appraisals, **/Berksfile, **/Brewfile, **/Buildfile, **/Capfile, **/Dangerfile, **/Deliverfile, **/Fastfile, **/*Fastfile, **/Gemfile, **/Guardfile, **/Jarfile, **/Mavenfile, **/Podfile, **/Puppetfile, **/Rakefile, **/rakefile, **/Schemafile, **/Snapfile, **/Steepfile, **/Thorfile, **/Vagrantfile
  • Check: rubocop --force-exclusion {{files}}
  • Check (list files): rubocop --force-exclusion --format files {{files}}
  • Fix: rubocop --force-exclusion --autocorrect {{files}}
  • Options: exclude = List("node_modules/**/*", "tmp/**/*", "vendor/**/*", ".git/**/*")

rubocop-server ​

Pkl: Builtins.rubocop_server

Ruby style guide (using RuboCop's --server mode)

  • Files: **/*.rb, **/*.arb, **/*.axlsx, **/*.builder, **/*.gemfile, **/*.gemspec, **/*.jb, **/*.jbuilder, **/*.mspec, **/*.opal, **/*.pluginspec, **/*.podspec, **/*.rabl, **/*.rake, **/*.rbw, **/*.ru, **/*.ruby, **/*.schema, **/*.spec, **/*.thor, **/.irbrc, **/.pryrc, **/.simplecov, **/buildfile, **/Appraisals, **/Berksfile, **/Brewfile, **/Buildfile, **/Capfile, **/Dangerfile, **/Deliverfile, **/Fastfile, **/*Fastfile, **/Gemfile, **/Guardfile, **/Jarfile, **/Mavenfile, **/Podfile, **/Puppetfile, **/Rakefile, **/rakefile, **/Schemafile, **/Snapfile, **/Steepfile, **/Thorfile, **/Vagrantfile
  • Check: rubocop --server --force-exclusion {{files}}
  • Check (list files): rubocop --server --force-exclusion --format files {{files}}
  • Fix: rubocop --server --force-exclusion --autocorrect {{files}}
  • Options: exclude = List("node_modules/**/*", "tmp/**/*", "vendor/**/*", ".git/**/*")

sorbet ​

Pkl: Builtins.sorbet

Type checker for Ruby

  • Files: ruby files
  • Check: srb tc {{files}}

standard-rb ​

Pkl: Builtins.standard_rb

Ruby Standard Style

  • Files: ruby files
  • Check: standardrb {{files}}
  • Fix: standardrb --fix {{files}}

Rust ​

cargo-check ​

Pkl: Builtins.cargo_check

Fast Rust type checking

  • Files: **/*.rs
  • Check: cargo check --quiet
  • Fix: cargo fix --allow-dirty --allow-staged --quiet

cargo-clippy ​

Pkl: Builtins.cargo_clippy

Rust linter

  • Files: **/*.rs
  • Check: cargo clippy --manifest-path {{workspace_indicator}} --quiet
  • Fix: cargo clippy --manifest-path {{workspace_indicator}} --fix --allow-dirty --allow-staged --quiet
  • Options: workspace_indicator = "Cargo.toml"

cargo-deny ​

Pkl: Builtins.cargo_deny

Rust dependency linter

  • Files: **/Cargo.toml, **/Cargo.lock, **/deny.toml, **/.deny.toml, **/deny.exceptions.toml, **/.deny.exceptions.toml
  • Check: cargo-deny --locked --workspace check
  • Options: workspace_indicator = "Cargo.toml", dir = "{{workspace}}"

cargo-fmt ​

Pkl: Builtins.cargo_fmt

Rust code formatter

  • Files: **/*.rs, **/rustfmt.toml, **/.rustfmt.toml
  • Check: cargo fmt --check --manifest-path {{workspace_indicator}}
  • Check (list files): cargo fmt --check --manifest-path {{workspace_indicator}} -- --files-with-diff
  • Fix: cargo fmt --manifest-path {{workspace_indicator}}
  • Options: workspace_indicator = "Cargo.toml"

rustfmt ​

Pkl: Builtins.rustfmt

Rust code formatter (standalone)

  • Files: **/*.rs
  • Check: rustfmt --check --edition 2024 {{files}}
  • Check (list files): rustfmt --check --edition 2024 --files-with-diff {{files}}
  • Fix: rustfmt --edition 2024 {{files}}

Secrets ​

betterleaks ​

Pkl: Builtins.betterleaks

A Better Secrets Scanner built for configurability and speed

  • Files: text files
  • Check: betterleaks dir --redact --verbose --no-banner {{files}}

gitleaks ​

Pkl: Builtins.gitleaks

Secret scanner for repository working trees

  • Check: gitleaks dir --redact --verbose --no-banner .

kingfisher ​

Pkl: Builtins.kingfisher

Secret scanner with live validation and 1,000+ rules

  • Files: text files
  • Check: kingfisher scan {{files}} --no-update-check --no-validate --quiet

Shell ​

shellcheck ​

Pkl: Builtins.shellcheck

Shell script analyzer

  • Files: **/*.sh, **/*.bash; or sh, bash files
  • Check: shellcheck {{files}}
  • Check (diff): shellcheck --format=diff {{files}}
  • Options: batch = true, check_after_diff = true

shellharden ​

Pkl: Builtins.shellharden

The corrective bash syntax highlighter

  • Files: bash, sh files
  • Check: shellharden --check {{files}}
  • Check (diff): hk util format-diff --no-stdin {{files}} -- shellharden --transform '{}'
  • Fix: shellharden --replace {{files}}
  • Options: batch = true

shfmt ​

Pkl: Builtins.shfmt

Shell formatter

  • Files: **/*.sh, **/*.bash, **/*.mksh, **/*.bats, **/*.zsh; or sh, bash files

  • Check (diff): shfmt -d --apply-ignore {{ files }}

  • Check (list files):

    sh
    files=$(shfmt -l --apply-ignore {{ files }})
    if [ -n "$files" ]; then
    echo "$files"
    exit 1
    fi
  • Fix: shfmt -w --apply-ignore {{ files }}

  • Options: batch = true

Special Purpose ​

byte-order-marker ​

Pkl: Builtins.byte_order_marker

Detect UTF-8 BOM

  • Files: text files
  • Check (diff): hk util check-byte-order-marker --diff {{files}}
  • Fix: hk util fix-byte-order-marker {{files}}

check-added-large-files ​

Pkl: Builtins.check_added_large_files

Prevent committing large files

  • Files: **/*
  • Check: hk util check-added-large-files {{files}}
  • Options: allow_binary = true

check-case-conflict ​

Pkl: Builtins.check_case_conflict

Detect case-insensitive filename conflicts

  • Files: **/*
  • Check: hk util check-case-conflict {{files}}
  • Options: allow_binary = true, allow_symlinks = true

check-conventional-commit ​

Pkl: Builtins.check_conventional_commit

Verify commit message matches conventional commits formatting

  • Check: hk util check-conventional-commit {{commit_msg_file}}

check-executables-have-shebangs ​

Pkl: Builtins.check_executables_have_shebangs

Verify executable files have shebang lines

  • Files: text files
  • Check: hk util check-executables-have-shebangs {{files}}

check-merge-conflict ​

Pkl: Builtins.check_merge_conflict

Detect merge conflict markers

  • Files: text files
  • Check: hk util check-merge-conflict --assume-in-merge {{files}}

check-shebang-scripts-are-executable ​

Pkl: Builtins.check_shebang_scripts_are_executable

Verify files with shebang lines are executable

  • Files: text files
  • Check: hk util check-shebang-scripts-are-executable {{files}}

Pkl: Builtins.check_symlinks

Detect broken symlinks

  • Files: **/*
  • Check: hk util check-symlinks {{files}}
  • Options: allow_symlinks = true

cocogitto-commit-msg ​

Pkl: Builtins.cocogitto_commit_msg

Conventional commits linter

  • Check: cog --quiet verify --file {{commit_msg_file}}

Pkl: Builtins.destroyed_symlinks

Detect symlinks replaced by regular files holding their target path

  • Check: hk util destroyed-symlinks

detect-private-key ​

Pkl: Builtins.detect_private_key

Detect accidentally committed private keys

  • Files: text files
  • Check: hk util detect-private-key {{files}}

fix-smart-quotes ​

Pkl: Builtins.fix_smart_quotes

Replace smart quotes with regular quotes

  • Files: text files
  • Check (diff): hk util fix-smart-quotes --diff {{files}}
  • Fix: hk util fix-smart-quotes {{files}}

forbid-submodules ​

Pkl: Builtins.forbid_submodules

Forbid git submodules in the repository

  • Check: hk util forbid-submodules

harper ​

Pkl: Builtins.harper

Grammar checker for prose and documentation

  • Files: text files
  • Check: harper-cli lint {{files}}

harper-commit-message ​

Pkl: Builtins.harper_commit_message

Grammar checker for commit message

  • Check: harper-cli lint {{commit_msg_file}}

hk-test ​

Pkl: Builtins.hk_test

Run step-defined hk's tests

  • Files: hk.pkl, .config/hk.pkl
  • Check: hk test --quiet

just-format ​

Pkl: Builtins.just_format

Justfile formatter

  • Files: **/justfile, **/*.just

  • Fix:

    sh
    xargs -d'
    ' -I {} -- just --fmt --unstable --justfile {}

ls-lint ​

Pkl: Builtins.ls_lint

Directory and filename naming linter

  • Files: **/*
  • Check: ls-lint
  • Options: allow_binary = true, allow_symlinks = true

lychee ​

Pkl: Builtins.lychee

Fast, async, stream-based link checker

  • Files: text files
  • Check: lychee --no-progress {{files}}

lychee-extended ​

Pkl: Builtins.lychee_extended

Flint's repository-aware link check (requires flint and lychee). See https://github.com/grafana/flint/blob/main/docs/linters/lychee.md

  • Files: text files
  • Check: flint checker lychee --files-from -

mixed-line-ending ​

Pkl: Builtins.mixed_line_ending

Detect and fix mixed line endings

  • Files: text files
  • Check (diff): hk util mixed-line-ending --diff {{files}}
  • Fix: hk util mixed-line-ending --fix {{files}}

newlines ​

Pkl: Builtins.newlines

Ensure files end with newline

  • Files: text files
  • Check (diff): hk util end-of-file-fixer --diff {{files}}
  • Fix: hk util end-of-file-fixer --fix {{files}}

no-commit-to-branch ​

Pkl: Builtins.no_commit_to_branch

Prevent direct commits to protected branches

  • Check: hk util no-commit-to-branch

renovate-deps ​

Pkl: Builtins.renovate_deps

Flint's repository-aware dependency check (requires flint and renovate). See https://github.com/grafana/flint/blob/main/docs/linters/renovate-deps.md

  • Files: **/*
  • Check: flint checker renovate-deps --files-from -
  • Fix: flint checker renovate-deps --fix --files-from -
  • Options: stage = List(".github/renovate-tracked-deps.json")

textlint ​

Pkl: Builtins.textlint

Pluggable natural language linter

  • Files: **/*.md, **/*.markdown, **/*.txt
  • Check: textlint {{files}}
  • Fix: textlint --fix {{files}}
  • Options: batch = true

trailing-whitespace ​

Pkl: Builtins.trailing_whitespace

Detect and remove trailing whitespace

  • Files: text files
  • Check (diff): hk util trailing-whitespace --diff {{files}}
  • Fix: hk util trailing-whitespace --fix {{files}}

typos ​

Pkl: Builtins.typos

Source code spell checker

  • Files: text files

  • Check (diff):

    sh
    status=0
    output=$(typos --force-exclude --diff {{files}}) || status=$?
    [ -z "$output" ] && exit "$status"
    printf "%s" "$output"
    exit 1
  • Fix: typos --force-exclude --write-changes {{ files }}

vale ​

Pkl: Builtins.vale

Prose linter for markup and code comments

  • Files: text files
  • Check: vale sync && vale {{ files }}

Typst ​

tinymist ​

Pkl: Builtins.tinymist

Typst linter (requires tinymist 0.14.20 or newer)

  • Files: **/*.typ

  • Check:

    sh
    code=0
    for f in {{ files }}; do
      tinymist lint "$f" || code=$?
    done
    exit "$code"
  • Options: batch = true, shell = "sh -c"

typstyle ​

Pkl: Builtins.typstyle

Typst formatter

  • Files: **/*.typ
  • Check (diff): typstyle --diff {{files}}
  • Fix: typstyle --inplace {{files}}

YAML ​

ryl ​

Pkl: Builtins.ryl

Re-implementation of yamllint in Rust

  • Files: yaml files
  • Check: ryl check {{files}}
  • Check (diff): ryl check --diff {{files}}
  • Check (list files): ryl check --list-files {{files}}
  • Fix: ryl check --fix {{files}}
  • Options: check_after_diff = true

Bring a tool of yer own aboard ​

If there's no builtin for yer tool, define a step with glob, check, and an optional fix command. Only enable batching if the tool can process independent subsets of the files correctly.

See custom steps for a complete example. And, as the song goes, all you hands who would sign aboard: see contributing to add a reusable definition to the standing crew.

MIT LicenseCopyright © 2026jdx.dev