Skip to content

Commit 66274a9

Browse files
committed
🚀 Add read-only release notes mode
- 📦 Replace the old tag-suggestion flow with a read-only release notes generator that prints grouped Markdown notes to stdout instead of emitting a `git tag` command. - 🧭 Rename the public flag, mode, config key, and prompt fallback to `release` so help text, dispatch, validation, and stats stay aligned. - 🔧 Simplify version comparison for update checks with a local parser that tolerates prefixes and suffixes, reducing tag-specific coupling. - 🧪 Refresh unit coverage for prompt loading, mode resolution, output behavior, and version parsing to protect the new release workflow. - 📝 Update the release command documentation to describe the new mode and its configuration, examples, and command reference.
1 parent 717dd68 commit 66274a9

41 files changed

Lines changed: 2531 additions & 1275 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/cd/.SRCINFO

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ pkgbase = cai
1212
makedepends = python-setuptools-scm
1313
depends = python
1414
depends = python-yaml
15-
depends = python-openai
15+
depends = python-pathspec
1616
depends = python-requests
1717
depends = python-typer
1818
depends = git

.github/cd/PKGBUILD

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ license=('MIT')
1010
depends=(
1111
python
1212
python-yaml
13-
python-openai
13+
python-pathspec
1414
python-requests
1515
python-typer
1616
git

.linters/.pylintrc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,8 @@ check-protected-access-in-special-methods=no
6969
max-args=30 # Max arguments per function
7070
max-attributes=12 # Max attributes per class
7171
max-branches=35 # Max branches per function
72-
max-locals=80 # Max locals per function
73-
max-returns=10 # Max return statements per function
72+
max-locals=90 # Max locals per function
73+
max-returns=15 # Max return statements per function
7474
max-statements=140 # Max total statements per function
7575
max-public-methods=20 # Max public methods per class
7676
min-public-methods=1 # Min public methods per class

docs/git-cai.txt

Lines changed: 181 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -10,29 +10,33 @@ git-cai - AI-powered commit message generator
1010
SYNOPSIS
1111
--------
1212
[verse]
13-
`git cai` [-A | --amend] [-a | --all] [-C | --conventional]
14-
[-b | --branch] [-c | --crazy] [-d | --debug]
15-
[-B | --allow-secrets] [-k | --check] [-n | --ping]
16-
[-e TEMPERATURE | --temperature TEMPERATURE]
13+
`git cai` [-A | --amend] [-a | --all]
14+
[-B | --allow-secrets] [-b | --branch | --no-branch]
15+
[-C | --conventional | --no-conventional] [-c | --crazy]
16+
[-D [HASH] | --explain [HASH]] [-d | --debug]
17+
[-E TEMPERATURE | --temperature TEMPERATURE]
18+
[-e | --emoji | --no-emoji]
1719
[-F | --full-files | --no-full-files] [-f PATH | --files PATH]
1820
[-g | --generate-config]
1921
[-H KEY=VALUE | --set-home KEY=VALUE] [-h | --help]
20-
[-I | --init]
21-
[-i | --install-completion]
22+
[-I | --init] [-i | --install-completion]
23+
[-k | --check]
24+
[-L CODE | --language CODE]
2225
[-l [config|editor|language|model|path|provider|style] |
2326
--list [config|editor|language|model|path|provider|style]]
2427
[-m MODEL | --model MODEL]
28+
[-N | --changelog] [-n | --ping]
2529
[-o | --signoff | --no-signoff]
2630
[-P PROVIDER | --provider PROVIDER] [-p | --generate-prompts]
2731
[--print]
32+
[-q true|false | --sql true|false]
33+
[-R | --release]
2834
[-r | --PR] [--base BRANCH]
2935
[-S KEY=VALUE | --set KEY=VALUE]
3036
[-s [N|HASH] | --squash [N|HASH]]
31-
[-q true|false | --sql true|false]
32-
[--style STYLE] [--language CODE] [--emoji | --no-emoji]
33-
[-T SECONDS | --timeout SECONDS]
34-
[-t | --time] [-u | --update] [-v | --version]
35-
[-x CONTEXT | --context CONTEXT]
37+
[-T SECONDS | --timeout SECONDS] [-t | --time]
38+
[-u | --update] [-v | --version] [-w | --split]
39+
[-x CONTEXT | --context CONTEXT] [-y STYLE | --style STYLE]
3640
[-z | --stats] [--since YYYY-MM-DD] [--json] [--reset-stats]
3741

3842

@@ -188,6 +192,24 @@ git cai -c
188192
git cai -A -c
189193
----
190194

195+
-D, --explain [HASH]::
196+
Explain what a change does in plain prose, printed to stdout. With no
197+
argument, explains the staged diff; with a commit HASH, explains that
198+
commit's diff. Read-only: nothing is staged, committed, or amended.
199+
+
200+
The explanation is prose, not a commit message: it covers the intent of
201+
the change, the notable edits, and any risks a reviewer should know. Use
202+
`-x` to add context the diff does not show.
203+
+
204+
The prompt follows the usual fallback chain: `explain_prompt_file` from
205+
config, then `~/.config/cai/explain_prompt.md`, then the built-in default.
206+
+
207+
----
208+
git cai --explain
209+
git cai -D a1b2c3d
210+
git cai -D -x "Fixes JIRA-1234"
211+
----
212+
191213
-d, --debug::
192214
Enable debug logging. Produces verbose output to stderr, including
193215
configuration loading, provider selection, prompt assembly, and the raw
@@ -200,7 +222,7 @@ Cannot be combined with `--help` or `--version`.
200222
git cai -d
201223
----
202224

203-
-e, --temperature TEMPERATURE::
225+
-E, --temperature TEMPERATURE::
204226
Override the sampling temperature of the active provider for this invocation
205227
only. Higher values produce more varied wording; lower values are more
206228
deterministic. The override is provider-scoped, mirroring `--model`, and does
@@ -209,8 +231,19 @@ temperature (for example OpenAI GPT-5 models and Anthropic Opus 4.7 and newer);
209231
for those the value is ignored and a warning is logged.
210232
+
211233
----
212-
git cai -e 0.7
213-
git cai -P anthropic -e 0.2
234+
git cai -E 0.7
235+
git cai -P anthropic -E 0.2
236+
----
237+
238+
-e, --emoji / --no-emoji::
239+
Override emoji usage for this invocation only. `--emoji` instructs the model
240+
to use relevant emojis; `--no-emoji` disables them even when the persisted
241+
config enables emoji. To change the default permanently, set `emoji:` in
242+
`cai_config.yml`.
243+
+
244+
----
245+
git cai --no-emoji
246+
git cai --emoji
214247
----
215248

216249
-F, --full-files::
@@ -341,13 +374,16 @@ git cai --check
341374
git cai --check --ping
342375
----
343376

344-
-n, --ping::
345-
Only meaningful together with `--check`. After the offline checks, send one
346-
small request to the active provider and report whether it is reachable and how
347-
long it took. Useful to confirm an API key and network path end to end.
377+
-L, --language CODE::
378+
Override the commit message language for this invocation only, using a
379+
supported language code (e.g. `de`, `fr`, `es`), or `none` to omit the
380+
language instruction. Run `git cai -l language` to list supported codes. An
381+
unsupported code is rejected with an error. To change it permanently, set
382+
`language:` in `cai_config.yml`.
348383
+
349384
----
350-
git cai --check --ping
385+
git cai --language de
386+
git cai --language none
351387
----
352388

353389
-l, --list [config|editor|language|model|path|provider|style]::
@@ -377,6 +413,38 @@ git cai -P anthropic -m claude-haiku-4-5
377413
git cai -P ollama -m mistral
378414
----
379415

416+
-N, --changelog::
417+
Generate a changelog section in the "Keep a Changelog" style from the
418+
commits since the most recent tag (`git describe --tags --abbrev=0`). If the
419+
repository has no tags yet, the whole history is used.
420+
+
421+
Entries are grouped under the standard headings (Added, Changed,
422+
Deprecated, Removed, Fixed, Security); headings without entries are
423+
omitted. Read-only: nothing is committed or tagged.
424+
+
425+
By default the section is printed to stdout. Set `changelog_to_file: true`
426+
to prepend it to the changelog file instead; the filename comes from
427+
`changelog_file_name` (default `CHANGELOG.md`) and the file is written in
428+
the repository root, keeping any existing content below the new section.
429+
+
430+
The prompt follows the usual fallback chain: `changelog_prompt_file` from
431+
config, then `~/.config/cai/changelog_prompt.md`, then the built-in default.
432+
+
433+
----
434+
git cai --changelog
435+
git cai -N > CHANGELOG.new.md
436+
git cai -S changelog_to_file=true
437+
----
438+
439+
-n, --ping::
440+
Only meaningful together with `--check`. After the offline checks, send one
441+
small request to the active provider and report whether it is reachable and how
442+
long it took. Useful to confirm an API key and network path end to end.
443+
+
444+
----
445+
git cai --check --ping
446+
----
447+
380448
-o, --signoff / --no-signoff::
381449
Append a `Signed-off-by:` trailer to the generated commit message
382450
using your configured git `user.name` and `user.email`. Applies in
@@ -459,6 +527,37 @@ git cai --sql true # record this commit even when config has stats off
459527
git cai -q false # don't record this commit even when config has stats on
460528
----
461529

530+
-R, --release::
531+
Print release notes for the commits since the most recent tag
532+
(`git describe --tags --abbrev=0`) to stdout. If the repository has no tags
533+
yet, the whole history is used. Read-only: nothing is committed or tagged.
534+
+
535+
The notes open with a one-line title, then group the changes under Markdown
536+
h3 headings (`### Features`) in this order: Breaking changes, Features, Bug
537+
fixes, Performance, Refactoring, Documentation, Tests, Build and CI, Chores,
538+
Reverts. A heading is printed only when it has entries -- a release that
539+
fixes no bugs shows no "Bug fixes" heading. Each commit is assigned by its Conventional Commits type
540+
(`feat` -> Features, `fix` -> Bug fixes, and so on); a `!` marker or a
541+
`BREAKING CHANGE` note goes under Breaking changes. Commits without a type
542+
prefix are placed by inferring the closest heading from the message.
543+
+
544+
Emojis appear only when they are switched on, either by `emoji: true` in
545+
`cai_config.yml` or by `-e`/`--emoji` on the command line. With `--no-emoji`,
546+
or with emoji disabled in config, the notes are plain text.
547+
+
548+
Output is the notes and nothing else, so it pipes cleanly into a file, a
549+
release form, or `git tag -a -F -`.
550+
+
551+
The prompt follows the usual fallback chain: `release_prompt_file` from
552+
config, then `~/.config/cai/release_prompt.md`, then the built-in default.
553+
+
554+
----
555+
git cai --release
556+
git cai -R --no-emoji
557+
git cai -R > RELEASE_NOTES.md
558+
git cai -R -x "First public release"
559+
----
560+
462561
-r, --PR::
463562
Generate a Pull Request description summarizing the commits between the
464563
current branch and its base branch. git-cai collects the commit messages
@@ -545,42 +644,6 @@ git cai -s 3
545644
git cai -s a1b2c3d
546645
----
547646

548-
--style STYLE::
549-
Override the commit message tone style for this invocation only (e.g.
550-
`professional`, `neutral`, `friendly`, `funny`, `excited`, `sarcastic`,
551-
`apologetic`, `academic`, or `none` to let the model choose). Run
552-
`git cai -l style` to see all styles with examples. An invalid value is
553-
rejected with an error. To change the style permanently, set `style:` in
554-
`cai_config.yml`.
555-
+
556-
----
557-
git cai --style funny
558-
git cai --style none
559-
----
560-
561-
--language CODE::
562-
Override the commit message language for this invocation only, using a
563-
supported language code (e.g. `de`, `fr`, `es`), or `none` to omit the
564-
language instruction. Run `git cai -l language` to list supported codes. An
565-
unsupported code is rejected with an error. To change it permanently, set
566-
`language:` in `cai_config.yml`.
567-
+
568-
----
569-
git cai --language de
570-
git cai --language none
571-
----
572-
573-
--emoji / --no-emoji::
574-
Override emoji usage for this invocation only. `--emoji` instructs the model
575-
to use relevant emojis; `--no-emoji` disables them even when the persisted
576-
config enables emoji. To change the default permanently, set `emoji:` in
577-
`cai_config.yml`.
578-
+
579-
----
580-
git cai --no-emoji
581-
git cai --emoji
582-
----
583-
584647
-T, --timeout SECONDS::
585648
Override the HTTP timeout for the LLM call in this invocation. The default
586649
is 30 seconds for remote providers and 300 seconds for Ollama (since local
@@ -635,6 +698,29 @@ from git tags via `setuptools-scm`.
635698
git cai -v
636699
----
637700

701+
-w, --split::
702+
Suggest how to break the staged change into several smaller, logically
703+
coherent commits. The grouping is at file granularity: whole files are
704+
assigned to groups, never split within a file. Each group is printed with a
705+
suggested one-line commit message and the file paths it contains.
706+
+
707+
Advisory: the plan is printed and nothing is executed. To apply a group,
708+
run `git reset`, then `git add <files>` and `git cai` for each group in
709+
turn.
710+
+
711+
Unlike the other read-only modes, the `--split` prompt does not receive the
712+
language, style, or emoji instructions: its output is a structured plan, not
713+
prose.
714+
+
715+
The prompt follows the usual fallback chain: `split_prompt_file` from
716+
config, then `~/.config/cai/split_prompt.md`, then the built-in default.
717+
+
718+
----
719+
git add -A
720+
git cai --split
721+
git cai -w -x "Keep the migration separate"
722+
----
723+
638724
-x, --context CONTEXT::
639725
Provide extra context for the LLM to consider when generating the commit
640726
message. The context string is appended to the diff (or commit history in
@@ -643,7 +729,8 @@ provider. This is useful for including information that is not visible in
643729
the diff itself, such as a ticket number, the reason for a change, or a
644730
link to an issue.
645731
+
646-
Can be combined with `COMMIT`, `AMEND`, `SQUASH`, or `PR` modes.
732+
Can be combined with `COMMIT`, `AMEND`, `SQUASH`, `PR`, `EXPLAIN`,
733+
`SPLIT`, `CHANGELOG`, and `TAG` modes.
647734
Cannot be used with `--list` or `--update`.
648735
+
649736
----
@@ -654,9 +741,25 @@ git cai --squash -x "Resolves JIRA-99"
654741
git cai -r -x "Closes JIRA-1234"
655742
----
656743

744+
-y, --style STYLE::
745+
Override the commit message tone style for this invocation only (e.g.
746+
`professional`, `neutral`, `friendly`, `funny`, `excited`, `sarcastic`,
747+
`apologetic`, `academic`, or `none` to let the model choose). Run
748+
`git cai -l style` to see all styles with examples. An invalid value is
749+
rejected with an error. To change the style permanently, set `style:` in
750+
`cai_config.yml`.
751+
+
752+
----
753+
git cai --style funny
754+
git cai --style none
755+
----
756+
657757
-z, --stats::
658-
Show local-only usage analytics: commits and squashes generated, top
659-
provider, total token counts, average latency, and per-provider rollups.
758+
Show local-only usage analytics: commits, amends, squashes and PR
759+
descriptions generated, top provider, total token counts, average latency,
760+
and per-provider rollups. The read-only modes (`--explain`, `--split`,
761+
`--changelog`, `--release`) are recorded in the database and included in the
762+
token and latency totals, but do not yet get their own count line.
660763
Recording is opt-in via the top-level `stats` config key (a plain
661764
boolean, default `false`), so this view is empty until you turn writing
662765
on. No diff content, commit messages, or file paths are stored — only
@@ -950,6 +1053,24 @@ Available configuration keys:
9501053
`--PR`. Follows the same fallback chain as `prompt_file` /
9511054
`squash_prompt_file`: configured path -> `~/.config/cai/pr_prompt.md` ->
9521055
built-in fallback.
1056+
- `changelog_file_name` -- filename used when `changelog_to_file` is `true`
1057+
(default `CHANGELOG.md`). The file is written in the repository root; any
1058+
existing content is kept below the newly generated section.
1059+
- `changelog_prompt_file` -- path to a custom Markdown prompt file used by
1060+
`--changelog` (fallback chain: configured path ->
1061+
`~/.config/cai/changelog_prompt.md` -> built-in fallback).
1062+
- `changelog_to_file` -- when running `--changelog`, prepend the generated
1063+
section to the changelog file instead of printing it to stdout
1064+
(`true`/`false`, default `false`)
1065+
- `explain_prompt_file` -- path to a custom Markdown prompt file used by
1066+
`--explain` (same fallback chain, with
1067+
`~/.config/cai/explain_prompt.md` as the middle step).
1068+
- `split_prompt_file` -- path to a custom Markdown prompt file used by
1069+
`--split` (same fallback chain, with `~/.config/cai/split_prompt.md` as
1070+
the middle step).
1071+
- `release_prompt_file` -- path to a custom Markdown prompt file used by
1072+
`--release` (same fallback chain, with `~/.config/cai/release_prompt.md` as
1073+
the middle step).
9531074
- `<provider>.model` -- model name for a specific provider
9541075
- `<provider>.temperature` -- temperature for a specific provider
9551076
- `anthropic.max_tokens` -- upper bound on Anthropic response tokens
@@ -958,7 +1079,8 @@ Available configuration keys:
9581079
(default `300`; overrides the global `timeout` for this provider only)
9591080
- `stats` -- opt in to local-only usage analytics (`true`/`false`,
9601081
default `false`). When enabled, every generation appends one row to
961-
`~/.local/share/git-cai/stats.db` capturing kind, repo name, provider,
1082+
`~/.local/share/git-cai/stats.db` capturing kind (`commit`, `amend`,
1083+
`squash`, `pr`, `explain`, `split`, `changelog`, `release`), repo name, provider,
9621084
model, token counts, real LLM latency, and a snapshot of the active
9631085
settings (language, style, emoji, temperature, prompt file). No diff
9641086
content, commit messages, or file paths are stored. View the rollup

0 commit comments

Comments
 (0)