diff --git a/AGENTS.md b/AGENTS.md index ec7d756c820c9b1297c6acd3bd49948a0c7a8e71..4a109b2a69e0963e92b089a3e4fdb672f185e793 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -10,11 +10,17 @@ This file provides guidance to AI coding agents when working with code in this r ## 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` +- **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` -- **Test**: `go test ./...` Example usage: @@ -27,16 +33,23 @@ just run -t feat -m "add validation" -T "Co-authored-by: Name " 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 using Markdown formatter +2. Body wrapping: strictly 72 columns with hanging indents for bullets/numbered lists 3. Trailer validation: follows git's trailer specification ## Architecture -Single-file CLI application (`main.go`) using: +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 (provides version handling, etc.) -- Future: Will need a Markdown formatter for body text (likely from Charmbracelet ecosystem based on dependencies) +- **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 @@ -48,14 +61,25 @@ When validation fails, clearly mark where the subject exceeds 50 characters in e ### Body Formatting -The body (`-b` flag) must be: +The body (`-b` flag) processing pipeline: -1. Sanitised because it should only be basic text and bullets -2. Formatted as Markdown, properly wrapping bullets with hanging indents on - following lines like this -3. Strictly wrapped to 72 columns using a pure-Go formatter -4. Separated from subject by one blank line -5. Separated from trailers by one blank line +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) @@ -79,9 +103,14 @@ Co-authored-by: This is a very long value, with spaces and ### 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 -- `-T` / `--trailer`: Repeatable flag for trailers +- `-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) + +Trailer format detail: Each `-T` flag takes a complete trailer string like `-T "Co-authored-by: Name "`, NOT separate key and value arguments. ### Final Output