Skip to content

Latest commit

 

History

History
224 lines (157 loc) · 8.16 KB

File metadata and controls

224 lines (157 loc) · 8.16 KB

Agent Guidelines

mbx build cache

mise install installs the configured mbx version. mise run activates the project's transparent Cargo wrapper, so compilation-heavy mise tasks and hk checks use ordinary cargo commands. Standalone Cargo commands require an activated mise shell. If the wrapper fails or creates a development papercut, rerun the exact equivalent command from CONTRIBUTING.md with MBX_DISABLE=1; this unblocks work without weakening the check. If bypassed Cargo succeeds, surface the mismatch and recommend a mr-boxington Discussion with the repository and commit, OS, mbx --version, mbx doctor, and both commands and outputs. Redact secrets, absolute cache paths, remote URLs, namespaces, and other sensitive or identifying details. Do not permanently disable the wrapper, and do not post externally without user authorization.

This file provides guidance to AI coding agents when working with code in this repository.

Conventional Commits

PR titles MUST follow conventional commit format. Intermediate commit subjects SHOULD use the same format:

Format: <type>(<scope>): <description>

Types:

  • feat: - New features
  • fix: - Bug fixes that affect the CLI behavior (not CI, docs, or infrastructure)
  • refactor: - Code refactoring
  • docs: - Documentation changes
  • style: - Code style/formatting (no logic changes)
  • perf: - Performance improvements
  • test: - Testing changes
  • chore: - Maintenance tasks, releases, dependency updates, CI/infrastructure changes
  • security: - Security-related changes
  • ci: - CI and automation changes
  • revert: - Reverting a previous change

Scopes:

  • For command-specific changes, use the command name: check, fix, run, init, install, validate, etc.
  • For subsystem changes: hook, step, config, lock, pkl, builtins, stash, deps

Description Style:

  • Start the description with a lowercase character
  • Use imperative mood ("add feature" not "added feature")
  • Keep it concise but descriptive

Examples:

  • fix(step): resolve race condition in file locking
  • feat(check): add --slow flag for expensive linters
  • feat(builtins): add biome linter
  • docs: update pkl configuration examples
  • chore: release 0.5.0

CI validates the pull request title and re-runs when it is edited. Intermediate commit subjects are not checked because pull requests are squash-merged. CI mechanically checks the allowed type, syntax, and lowercase-leading description; imperative mood remains a review rule.

Dependency Updates

  • Use the lowest compatibility-significant specificity in Cargo.toml (for example, "1" for stable 1.x dependencies).
  • When the existing manifest requirement accepts a routine dependency update, change only Cargo.lock.
  • Keep lockfile updates focused and avoid unrelated transitive dependency churn.

Development Commands

Build the project:

mise run build

Run tests:

# Run all tests (Rust unit tests + bats integration tests)
mise run test

# Run only Rust tests
mise run test:cargo

# Run a single Rust test by name
cargo test test_name

# Run only bats tests
mise run test:bats

# Run a specific bats test file
mise run test:bats test/check.bats

Lint and format code:

# Run all linters and checks
hk check --all
hk check --all --slow  # includes slower checks (cargo clippy)

# Fix formatting and linting issues
hk fix --all
hk fix --all --slow

High-Level Architecture

hk is a git hook manager and project linting tool written in Rust with emphasis on performance and concurrent execution. The architecture leverages file locks to maximize concurrency while preventing race conditions.

Crate Structure

The root Cargo package builds the hk CLI and the generate-docs utility. It depends on separately published crates for shared functionality:

  • xx: HTTP client and utilities
  • clx: CLI/terminal UI utilities (progress indicators, styling)
  • ensembler: Script/command execution engine

Core Components

Configuration System (src/config.rs):

  • Main config file: hk.pkl in project root
  • Uses Pkl (github.com/apple/pkl) as the configuration language
  • Config amends a base schema from pkl/Config.pkl

Hook System (src/hook.rs):

  • Manages git hooks (pre-commit, pre-push, commit-msg, prepare-commit-msg)
  • Supports custom hooks like "check" and "fix" for manual runs
  • Implements stashing strategies for git hooks
  • Handles concurrent step execution with proper locking

Step Execution (src/step/):

  • Steps are individual linting/formatting tasks
  • Each step can have: check, fix, shell commands
  • Steps support glob patterns for file filtering
  • Steps can depend on other steps
  • Steps use read/write file locks to prevent conflicts

File Locking (src/file_rw_locks.rs):

  • Implements a sophisticated file locking system
  • Allows multiple readers or single writer per file
  • Prevents race conditions during concurrent execution
  • Critical for maximizing parallelism

Built-in Linters (pkl/builtins/):

  • Extensive library of pre-configured linters and formatters
  • Each builtin is a Pkl file defining step configuration
  • Used via Builtins.linter_name in hk.pkl

CLI Interface (src/cli/):

  • Subcommands: init, install, uninstall, check, fix, run, validate, config
  • Uses usage-rs for argument parsing
  • Supports running specific hooks or steps

Documentation

  • Preview or build the website with mise run docs or mise run docs:build.
  • Edit generated reference content at its source: pkl/Config.pkl, settings.toml, builtin definitions, and Rust CLI help comments.
  • scripts/enrich-cli-docs.py adds maintained examples after CLI reference generation.
  • Example pages include docs/public/*.pkl directly. Validate them with scripts/generate-examples.sh in the mise environment.

Key Design Patterns

  1. Concurrent Execution: Steps run in parallel when possible, using tokio for async runtime
  2. File-based Coordination: Uses in-memory read/write locks keyed by file path to coordinate steps within a hook run
  3. Pluggable Configuration: Pkl-based config allows easy extension and customization
  4. Progressive Enhancement: Works with or without git, libgit2, mise, etc.

Integration Points

  • Git Integration: Can use either libgit2 or shell git commands (controlled by HK_LIBGIT2 env var, default: true)
  • Mise Integration: Deeply integrated with mise for task running and tool management
  • Tool Discovery: Automatically finds tools via PATH or mise shims

Testing

Bats integration tests are in test/*.bats. Each test file uses a common setup pattern:

setup() {
    load 'test_helper/common_setup'
    _common_setup
}

Tests run in isolated temp directories with a clean git repo. The $PKL_PATH variable points to the pkl config directory for amending Config.pkl.

Testing Builtins

Builtins should have pkl-level tests defined via the tests field on the Step (see pkl/Config.pkl StepTest). These tests are run by hk test and exercised in CI via test/builtins_tests.bats, which loads all builtins and runs their tests.

Tool stubs in test/builtin_tool_stubs/ use mise tool-stub to auto-install the correct tool version on demand. Each stub is a small script:

#!/usr/bin/env -S mise tool-stub
version = "2"
tool = "aqua:golangci/golangci-lint"

To add a new builtin with tests:

  1. Define the builtin in pkl/builtins/<name>.pkl with a tests block
  2. Add a tool stub in test/builtin_tool_stubs/<tool-name> if the tool isn't already available
  3. Use the TestMaker helper from pkl/builtins/test/helpers.pkl for standard check/fix test patterns
  4. Run hk test --step <step_name> to verify, or mise run test:bats test/builtins_tests.bats to run all builtin tests

GitHub Interactions

When AI contributes GitHub content—including a pull request description, review, pull request comment, or discussion post—append this disclosure:

*AI-assisted — Tool: <tool>; model: <provider>/<model>; version: <version-or-unavailable>.*

Use the exact model and version identifiers exposed by the runtime. Never infer or guess them; use unavailable when either value is not exposed.