git-ai 1.2.1

Git AI: Automates commit messages using ChatGPT. Stage your files, and Git AI generates the messages.
Documentation
# Changelog

All notable changes to this project are documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.2.1] - 2026-06-17

### Fixed

- **Commit hook now uses the configured API key**: the default generation path only
  attempted the OpenAI multi-step request when the `OPENAI_API_KEY` environment variable
  was set, silently ignoring a key stored via `git ai config set openai-api-key`. As a
  result every commit fell through to the local programmatic generator and emitted
  filename-derived subjects like `Update <file>`. The client is now built from the stored
  configuration (honoring a custom `openai-base-url` too), falling back to the environment
  variable only when the config holds no usable key.
- **Local-fallback truncation no longer splits words**: oversized fallback subjects are
  trimmed to the last whole word (no more `Update controlle`) instead of a hard mid-word cut.

## [1.2.0] - 2026-06-17

### Added

- **Configurable OpenAI model**: any model string is now accepted (not just the built-in set)
  and is verified to exist (via the OpenAI/compatible `models` API) at `git ai config set model`
  time — best-effort, so offline users aren't blocked. Unknown models fall back to a safe
  tokenizer/context size.
- **Custom OpenAI base URL** (`git ai config set openai-base-url <url>`) — point git-ai at a
  local or self-hosted OpenAI-compatible endpoint (e.g. ollama).

### Changed

- **Default model is now `gpt-4.1-mini`** (was `gpt-4.1`) — near-equivalent quality for short,
  structured commit messages at lower latency and cost on a per-commit tool.
- **Faster large diffs**: truncation now tokenizes once and decodes a token prefix instead of
  re-tokenizing on every binary-search step; parallel diff processing reworked to be both
  deterministic and correct.
- **Deterministic commit messages**: identical staged changes now always produce the same patch
  (and thus message) — previously affected by `HashMap` iteration order.
- Tightened the commit-message prompts (imperative mood, no trailing period, impact-first,
  anti-hallucination), with invariant tests.
- Upgraded all dependencies including async-openai 0.41, git2 0.21, tiktoken-rs 0.12, reqwest 0.13;
  pinned the Rust nightly toolchain for reproducible builds.
- Removed dead code (orphaned `ollama`/`client`/`profile` modules and the unused `model::run`).
- Honor the configured `max-commit-length` in the final message selection (was hardcoded to 72).

### Security

- Added `cargo audit` and qlty to CI; hardened all GitHub workflows (least-privilege permissions,
  SHA-pinned actions, fixed script-injection) and added datajust-style PR governance workflows.

## [1.1.2] - 2026-06-16

### Fixed

- **Multi-step pipeline broken since v1.1.1**: `collect_diff_data()` dropped git
  file/hunk header lines (libgit2 origins `'F'`/`'H'`), so the generated patch had no
  `diff --git`/`@@` markers. `parse_diff()` could then no longer split the diff into
  per-file sections and collapsed every commit into a single `"unknown"` file, producing
  generic commit messages. Headers are now preserved. (#91)
- Annotated an `f32` literal in `model::run()` so the crate builds clean under the latest
  nightly (the new `float_literal_f32_fallback` lint).

### Tests

- Added real-git2 regression tests (`tests/patch_test.rs`) asserting headers are preserved
  and that `to_patch()` output parses back into the correct per-file sections (never
  `"unknown"`).