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 asformatted-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 testorgo 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:
- Subject length: max 50 characters in format
type(scope): message - Body wrapping: strictly 72 columns with hanging indents for bullets/numbered lists
- 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:
- Sanitization:
bluemonday.UGCPolicy()strips dangerous HTML/scripts while preserving basic formatting - 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
- Bullets (
- Word wrapping algorithm: Greedy wrapping splits on word boundaries, never mid-word
- Hanging indent logic: For bullets/numbered lists, first line gets the marker, continuation lines get spaces equal to marker width
- 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 inKey: valueformat (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.