Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Auto-detection

When you omit provider (or set provider=auto), Clover infers the provider and its parameters from the content of the target line. Common cases need almost no annotation.

# @clover: constraint=minor
FROM redis:7.2.0

Here Clover recognizes a Docker image reference on the line and resolves it with the Docker provider, inferring repository=redis. A line that names a GitHub repository resolves with the GitHub provider instead.

The @clover shorthand

The canonical spelling of an auto marker is a bare @clover comment. The @ sigil stands for provider=auto, and the colon returns when other keys follow.

# @clover
FROM redis:7.2.0
# @clover: constraint=minor
FROM redis:7.2.0

The longhand clover: provider=auto means the same thing and stays valid. The shorthand is what clover annotate writes, and clover format rewrites the longhand into it. A redundant @clover: provider=<name> unfolds back to longhand, since an explicit provider always wins.

Recognized shapes

Auto-detection recognizes these shapes:

ShapeFileProviderInference and notes
uses: reference (commit SHA or tag)GitHub Actions YAMLGitHubrepository
FROM instruction (tag or digest)Dockerfile, ContainerfileDockerregistry, repository
image: mapping (tag or digest)YAMLDockerregistry, repository
uses: docker:// container job (tag or digest)Workflow YAMLDockerregistry, repository
Digest pin with a floating tag (nonroot, latest)Any Docker shape aboveDockerInfers track, so the digest stays fresh while the tag text stays put
component: include (gitlab.com/group/project/name@1.0.0)GitLab CI/CD YAMLGitLabrepository, plus host on a self-managed instance
terraform = "1.9.8" tool versionmise, asdfHashiCorpproduct
node (or the mise alias nodejs) tool versionmise, asdfNode.js-
go (or golang) tool versionmise, asdfGo-
python tool versionmise, asdfPython-
rust tool versionmise, asdfRust-
swift tool versionmise, asdfSwift-
zig tool versionmise, asdfZig-
github: or ubi: backend, or a GitHub-released registry tool (tofu, ripgrep, …)mise, asdfGitHubrepository
A registry tool released only on PyPI (ansible, yamllint, …)mise, asdfPyPIpackage
A registry tool released only on npm (prettier, wrangler, …)mise, asdfnpmpackage
A registry tool released only on crates.io (magika, …)mise, asdfcrates.iopackage
go and toolchain directivesgo.modGoResolved from the go.dev download index
.python-version pin (whole line)pyenvPythonNo comment syntax, so tracked through a sidecar or clover run --infer. An implementation-prefixed pin like pypy3.10-7.3.12 infers nothing
.swift-version pin (whole line)swiftlySwiftNo comment syntax, so tracked through a sidecar or clover run --infer. A snapshot pin like 6.1-snapshot-2026-06-29 infers nothing
target-version (compact py314 form)pyproject.toml (ruff, black, mypy)Python-
requires-python floorpyproject.tomlPythonBumped in place, so ">=3.14" keeps its operator and precision. A range like ">=3.10,<4" carries two tokens and is skipped as ambiguous
Quoted dependency specifier ("uv_build>=0.8.24")pyproject.toml dependencies, requires, or dependency-group arrayPyPIpackage. Bumped in place with any trailing environment marker left untouched. Only ==, >=, ~=, === are recognized, since bumping an exclusion (!=) or a cap (<, <=) would invert its meaning. A range (">=1.26,<2.1") or a multi-specifier line is skipped as ambiguous
channel = "1.97.0"rust-toolchain.tomlRustA named channel (stable, nightly-2026-07-11) carries no version and infers nothing
rust-version floorCargo.tomlRustBumped in place with precision preserved, so "1.70" advances only when a new minor line ships
required_version constraint.tf (Terraform)HashiCorpproduct=terraform, bumped in place so "~> 1.11.0" keeps its operator and precision. In a .tofu file it tracks OpenTofu through the GitHub provider instead. In .tf OpenTofu files, set provider explicitly
version constraint in a required_providers entry.tf (Terraform)TerraformReads the sibling source by parsing the file as HCL, the one inference that looks beyond the target line. A version outside required_providers infers nothing. In a .tofu file it resolves with provider=opentofu against the OpenTofu registry. In .tf OpenTofu files, set provider=opentofu explicitly

The mise rows cover any of the shapes mise reads (mise.toml, .mise.toml, a mise.local.toml or mise.<env>.toml variant, mise/config.toml, .config/mise/config.toml, a .config/mise/conf.d fragment) and asdf’s .tool-versions file, which mise reads with the same tool names. The tool maps are generated from the mise registry. A tool with a GitHub-shaped backend tracks its repository, and one released only through pipx, npm, or cargo tracks the package that ecosystem installs.

When to be explicit

Auto-detection covers the obvious cases. Set provider explicitly when:

  • the line is ambiguous, or the value you want to track is not the most obvious token on it,
  • you are tracking something that is not literally written on the target line,
  • you want the annotation to document intent regardless of the line’s contents.

Inference only fills in what you leave out, and any key you set yourself always wins.

Generating annotations

To add @clover directives across an existing codebase rather than write them by hand, run clover annotate. It scans for the same lines auto-detection recognizes and inserts a directive above each, so onboarding a repository is a single command.

To skip annotations entirely, run clover run --infer: it updates every recognized line directly, as if each carried a bare @clover, writing no comments at all. Annotations remain the way to pin selection rules (constraint, include, cooldown, …) to a line.