AGENTS.md

AGENTS.md

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

Development Commands

  • Default workflow: just (runs fmt, lint, staticcheck, test, vuln, reuse)
  • Build: just build (outputs binary as formatted-commit)
  • Run during development: just run [flags]
  • Format code: just fmt (uses gofumpt)
  • Lint: just lint (uses golangci-lint)
  • Static analysis: just staticcheck
  • Vulnerability check: just vuln (uses govulncheck)
  • License compliance: just reuse (REUSE specification)
  • Test: just test or go test ./...
  • Single test: go test -v -run TestName ./... (no tests exist yet)
  • Update dependencies: go mod tidy

Example usage:

just run -t feat -m "add validation" -T "Assisted-by: GLM 4.6 via Crush"

Project Purpose

This is a CLI tool that formats git commit messages according to Conventional Commits specification and pipes them directly to git commit -F -. It enforces:

  1. Subject length: max 50 characters in format type(scope): message
  2. Body wrapping: strictly 72 columns with hanging indents for bullets/numbered lists
  3. Trailer validation: follows git's trailer specification

Architecture

Multi-file CLI application split by concern:

  • main.go: Cobra CLI setup, flag definitions, subject validation, orchestration, git command execution
  • trailers.go: Trailer validation and block building following git's RFC 822 folding specification
  • wrapBody.go: Body text sanitization and custom word-wrapping with hanging indent support

Dependencies:

  • cobra: CLI framework for flags and commands
  • fang: Charmbracelet's execution wrapper (version handling, etc.)
  • bluemonday: HTML/Markdown sanitization using UGCPolicy
  • Custom word wrapping: Pure-Go implementation for 72-column wrapping with hanging indents

Critical Implementation Details

Subject Validation (50 char limit)

The subject is constructed as type(scope): message or type: message (when scope is empty). Breaking changes add a ! after the type/scope like type(scope)!: message.

When validation fails, clearly mark where the subject exceeds 50 characters in error output.

Body Formatting

The body (-b flag) processing pipeline:

  1. Sanitization: bluemonday.UGCPolicy() strips dangerous HTML/scripts while preserving basic formatting
  2. Line-by-line processing: Each line is processed based on its type:
    • Bullets (- or * ): Wrapped with 2-space hanging indent for continuation lines
    • Numbered lists (^\d+\.\s): Wrapped with hanging indent matching the marker length (e.g., 1. , 10. )
    • Plain text: Standard word-wrap at 72 columns
    • Blank lines: Preserved as-is
  3. Word wrapping algorithm: Greedy wrapping splits on word boundaries, never mid-word
  4. Hanging indent logic: For bullets/numbered lists, first line gets the marker, continuation lines get spaces equal to marker width
  5. Spacing: Body separated from subject by one blank line, from trailers by one blank line

Example wrapped bullet:

- This is a long bullet point that exceeds 72 characters and will
  be wrapped with proper hanging indent alignment on continuation
  lines.

Trailer Formatting (Critical)

Trailers follow git's specification precisely:

  • Format: Key: value (newline-delimited pairs)
  • No whitespace before or inside the key
  • Any number of spaces/tabs allowed between key and separator :
  • Values can be multiline with continuation lines starting with whitespace (RFC 822 folding)
  • The trailer group must be:
    • At the end of input, OR
    • The last non-whitespace lines before a line starting with ---
    • Preceded by one or more empty/whitespace-only lines

Example valid trailer:

Assisted-by: This is a very long value, with spaces and
  newlines in it.

Flag Nuance

  • -t / --type: Commit type (required) - e.g., feat, fix, refactor
  • -m / --message: Commit message (required) - the description after the colon
  • -s / --scope: Commit scope (optional) - goes in parentheses after type
  • -B / --breaking: Boolean flag for breaking changes (adds ! to subject)
  • -b / --body: String flag for commit body text (can use heredoc for multiline)
  • -T / --trailer: Repeatable flag accepting full trailer strings in Key: value format (not separate key/value args)
  • -a / --amend: Boolean flag to amend the previous commit instead of creating a new one

Trailer format detail: Each -T flag takes a complete trailer string like -T "Assisted-by: GLM 4.6 via Crush", NOT separate key and value arguments.

Final Output

The formatted commit message must be piped to git commit -F - to read from stdin. When the -a/--amend flag is used, it pipes to git commit --amend -F - instead.