Design

Pipeline

git diff --staged -M -U2 -> parse_diff -> prepare (filter, rank, fit) -> prompt (+ style, scopes, branch) -> Haiku structured output -> format_commit -> print / commit / hook.

The model only sees compact, pre-digested input and returns a small JSON object, which keeps cost near $0.0002 per commit.

Hunk ranking

Each hunk gets a score from its changed lines:

Worked example: adding def list_users(limit=50) to server.py scores 0.15 + 6 = 6.15; a hunk that only edits a comment scores 0.

Budget fitting

Budget is in estimated tokens (ceil(chars / 4)).

  1. Lock, generated and binary files become one-line summaries, never diffs. File headers and summaries are a fixed cost.
  2. Hunks are taken highest-score first if they fit whole (first fit; a too-big hunk is skipped, smaller ones still get a chance).
  3. If at least 40 tokens remain, the best skipped hunk is truncated to fit and ends with … N more lines.
  4. Output keeps original file and hunk order. Files with dropped hunks get … N more hunk(s) omitted (+a -d).

Example with budget 70: a 200-line hunk and two 2-line hunks with new functions: both functions are kept, the big hunk is reported as omitted.

Scope inference

packages/api/x.ts -> api (monorepo roots: packages, apps, libs, crates, services, modules, plugins). Otherwise leading src/lib is stripped and the first directory is used; tests/ maps to test; root files use their stem (except README/LICENSE/CHANGELOG/dotfiles). Candidates are ranked by changed lines. If the config lists allowed scopes, they replace the candidates in the prompt and invalid model scopes are dropped.

Style learning

From git log -50 --format=%s (merges and reverts ignored): share matching type(scope)!: subject, top types and scopes, majority capitalization of the first letter, and whether tickets appear in subjects. A ticket from the branch name (feature/ABC-123-x) becomes a Refs: ABC-123 footer unless the model already mentioned it. Output is always Conventional Commits; history only steers type, scope and capitalization.

Validation

Cache

Key: sha256 of [model, system prompt, user prompt]. The prompt contains the filtered diff, so any staged change misses the cache. Only the parsed model output is stored; warnings are recomputed on every run.

Hook rules

The hook is a three-line shell script calling python -m commitcraft --hook-run FILE SOURCE. Sources merge, squash, commit (amend and -c) and message are skipped, as is any file that already has non-comment text. Any error is swallowed so commits are never blocked.