git-cai-cli 0.12.2__tar.gz → 0.13.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/.linters/.flake8 +1 -1
  2. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/.linters/.pylintrc +1 -1
  3. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/.semgrepignore +1 -0
  4. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/PKG-INFO +40 -1
  5. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/README.md +39 -0
  6. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/cai_config.yml +3 -0
  7. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/docs/git-cai.txt +89 -5
  8. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/docs/man/git-cai.1 +149 -7
  9. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/src/git_cai_cli/_version.py +3 -3
  10. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/src/git_cai_cli/cli/cli.py +21 -3
  11. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/src/git_cai_cli/cli/helptext.py +3 -1
  12. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/src/git_cai_cli/cli/modes.py +17 -6
  13. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/src/git_cai_cli/core/config.py +25 -4
  14. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/src/git_cai_cli/core/gitutils.py +43 -0
  15. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/src/git_cai_cli/core/llm.py +62 -1
  16. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/src/git_cai_cli/core/options.py +2 -0
  17. git_cai_cli-0.13.1/src/git_cai_cli/core/pr.py +145 -0
  18. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/src/git_cai_cli/core/prompts_fallback.py +70 -9
  19. git_cai_cli-0.13.1/src/git_cai_cli/core/spinner.py +130 -0
  20. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/src/git_cai_cli/core/validate.py +3 -0
  21. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/src/git_cai_cli/main.py +13 -0
  22. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/src/git_cai_cli.egg-info/PKG-INFO +40 -1
  23. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/src/git_cai_cli.egg-info/SOURCES.txt +3 -0
  24. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/tests/integration/test_modes_integration.py +53 -8
  25. git_cai_cli-0.13.1/tests/integration/test_pr_integration.py +139 -0
  26. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/tests/unit/test_amend.py +9 -3
  27. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/tests/unit/test_modes.py +40 -8
  28. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/tests/unit/test_options.py +13 -0
  29. git_cai_cli-0.13.1/tests/unit/test_pr.py +310 -0
  30. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/tests/unit/test_prompt_loading.py +133 -0
  31. git_cai_cli-0.13.1/tests/unit/test_spinner.py +251 -0
  32. git_cai_cli-0.12.2/src/git_cai_cli/core/spinner.py +0 -53
  33. git_cai_cli-0.12.2/tests/unit/test_spinner.py +0 -91
  34. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/.caiignore +0 -0
  35. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/.gitattributes +0 -0
  36. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/.github/cd/.SRCINFO +0 -0
  37. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/.github/cd/PKGBUILD +0 -0
  38. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/.github/ci/_version.py +0 -0
  39. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/.github/ci/cai_config.ci.yml +0 -0
  40. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/.github/ci/tokens.ci.yml +0 -0
  41. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/.github/workflows/python-tests.yml +0 -0
  42. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/.github/workflows/release.yml +0 -0
  43. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/.github/workflows/release_aur.yml +0 -0
  44. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/.gitignore +0 -0
  45. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/.linters/.bandit.yml +0 -0
  46. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/.linters/.checkov.yml +0 -0
  47. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/.linters/.ls-lint.yml +0 -0
  48. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/.linters/.markdown-link-check.json +0 -0
  49. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/.linters/.markdownlint.json +0 -0
  50. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/.linters/.proselintrc +0 -0
  51. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/.linters/.yamllint.yml +0 -0
  52. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/.linters/check_git_branch_name.sh +0 -0
  53. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/.linters/lychee.toml +0 -0
  54. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/.linters/pyrightconfig.json +0 -0
  55. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/.markdownlintignore +0 -0
  56. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/.mega-linter.yml +0 -0
  57. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/.trivyignore +0 -0
  58. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/CLAUDE.md +0 -0
  59. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/LICENSE +0 -0
  60. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/Makefile +0 -0
  61. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/pyproject.toml +0 -0
  62. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/setup.cfg +0 -0
  63. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/src/git_cai_cli/__init__.py +0 -0
  64. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/src/git_cai_cli/cli/__init__.py +0 -0
  65. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/src/git_cai_cli/core/__init__.py +0 -0
  66. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/src/git_cai_cli/core/completion.py +0 -0
  67. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/src/git_cai_cli/core/editors.py +0 -0
  68. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/src/git_cai_cli/core/languages.py +0 -0
  69. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/src/git_cai_cli/core/squash.py +0 -0
  70. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/src/git_cai_cli.egg-info/dependency_links.txt +0 -0
  71. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/src/git_cai_cli.egg-info/entry_points.txt +0 -0
  72. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/src/git_cai_cli.egg-info/requires.txt +0 -0
  73. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/src/git_cai_cli.egg-info/top_level.txt +0 -0
  74. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/tests/conftest.py +0 -0
  75. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/tests/integration/test_cli_integration.py +0 -0
  76. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/tests/integration/test_config_integration.py +0 -0
  77. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/tests/integration/test_gitutils_integration.py +0 -0
  78. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/tests/integration/test_options_integration.py +0 -0
  79. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/tests/integration/test_squash_integration.py +0 -0
  80. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/tests/unit/test_branch_context.py +0 -0
  81. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/tests/unit/test_cli.py +0 -0
  82. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/tests/unit/test_completion.py +0 -0
  83. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/tests/unit/test_config.py +0 -0
  84. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/tests/unit/test_conventional.py +0 -0
  85. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/tests/unit/test_gitutils.py +0 -0
  86. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/tests/unit/test_helptext.py +0 -0
  87. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/tests/unit/test_llm.py +0 -0
  88. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/tests/unit/test_main.py +0 -0
  89. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/tests/unit/test_set_config.py +0 -0
  90. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/tests/unit/test_squash.py +0 -0
  91. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/tests/unit/test_validate.py +0 -0
  92. {git_cai_cli-0.12.2 → git_cai_cli-0.13.1}/uv.lock +0 -0
@@ -1,5 +1,5 @@
1
1
  [flake8]
2
- #extend-ignore = E501, F821
2
+ extend-ignore = E203
3
3
  exclude =
4
4
  test_copy_files_fsb.py,
5
5
  __pycache__
@@ -41,7 +41,7 @@ include-naming-hint=yes # Suggest the expected format when na
41
41
  max-line-length=256 # Maximum line length
42
42
  indent-string=' ' # Use 4 spaces per indentation level
43
43
  indent-after-paren=4 # Hanging indent size
44
- max-module-lines=1000 # Max lines per module before warning
44
+ max-module-lines=2000 # Max lines per module before warning
45
45
  ignore-long-lines=^\s*(# )?<?https?://\S+>?$
46
46
  # Ignore long lines if they’re URLs or comments
47
47
  expected-line-ending-format=LF # Require LF line endings
@@ -2,4 +2,5 @@
2
2
  cli.py
3
3
  config.py
4
4
  squash.py
5
+ pr.py
5
6
  release_aur.yml
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: git-cai-cli
3
- Version: 0.12.2
3
+ Version: 0.13.1
4
4
  Summary: Use LLM to create git commit messages
5
5
  Author-email: Thorsten Foltz <thorsten.foltz@live.com>
6
6
  License-Expression: MIT
@@ -81,6 +81,7 @@ Currently supported providers:
81
81
  - Conventional Commits format support
82
82
  - Change configuration from the command line
83
83
  - Optional commit squashing with automatic summary generation (all, last N, or up to a specific commit)
84
+ - Pull Request description generator (`--PR`) that summarizes the commits between the current branch and its base
84
85
  - List providers, models, active config, and file paths
85
86
  - Token usage logging for API calls
86
87
  - Branch name as LLM context
@@ -233,6 +234,9 @@ git cai -g
233
234
  - `conventional` – use Conventional Commits format
234
235
  - `token_logging` – log token usage after each LLM call
235
236
  - `measure_time` – log generation time
237
+ - `pr_to_file` – when `--PR` is used, write the generated description to a Markdown file in the repo root instead of stdout (default `false`)
238
+ - `pr_file_name` – filename used when `pr_to_file` is `true` (default `PR_DESCRIPTION.md`)
239
+ - `pr_prompt_file` – optional path to a custom Markdown prompt for `--PR` (falls back to `~/.config/cai/pr_prompt.md`, then a built-in default)
236
240
 
237
241
  ---
238
242
 
@@ -256,6 +260,8 @@ In addition to `git cai`, the following options are available:
256
260
  - `-m`, `--model` – override the model for this invocation (requires `-P`)
257
261
  - `-p`, `--generate-prompts` – generate default `commit_prompt.md` and `squash_prompt.md` in the current directory (for customization)
258
262
  - `-P`, `--provider` – override the LLM provider for this invocation
263
+ - `-r`, `--PR` – generate a Pull Request description from the commits between the current branch and its base (prints to stdout by default; set `pr_to_file=true` to write a Markdown file)
264
+ - `--base` `BRANCH` – explicit base branch for `--PR` (overrides auto-detection: `origin/HEAD` → `main` → `master`)
259
265
  - `-S`, `--set` – set a config value (`key=value`) in repo config (requires existing repo config)
260
266
  - `-s`, `--squash` `[N|HASH]` – squash commits on the current branch and summarize them. Without argument: squash all since branch checkout. With a number: squash the last N commits. With a commit hash: squash up to and including that commit
261
267
  - `-T`, `--timeout` `SECONDS` – HTTP timeout for this invocation (overrides config)
@@ -317,6 +323,39 @@ Persist the default:
317
323
  git cai -S full_files=true
318
324
  ```
319
325
 
326
+ ### Pull Request descriptions
327
+
328
+ `-r` / `--PR` generates a Markdown Pull Request description from the commits
329
+ between the current branch and its base branch. The output has two sections —
330
+ `## Summary` (bullet list following the same best practices as commit messages:
331
+ imperative mood, capitalized, no trailing period, 72-char wrap) and
332
+ `## Test plan` (a checklist a reviewer can run).
333
+
334
+ This mode never modifies git state — no commit, no reset, no force push.
335
+
336
+ ```sh
337
+ git cai -r # print to stdout (default)
338
+ git cai --PR # long form
339
+ git cai -r --base develop # explicit base branch
340
+ git cai -r -x "Closes JIRA-1234" # add extra context
341
+ ```
342
+
343
+ The base branch is auto-detected in this order: `origin/HEAD`, then local
344
+ `main`, then local `master`. Use `--base` for repositories with non-standard
345
+ layouts or no `origin/HEAD`.
346
+
347
+ By default the description is printed to stdout. To write it to a file in the
348
+ repository root instead, set `pr_to_file: true`:
349
+
350
+ ```sh
351
+ git cai -S pr_to_file=true
352
+ git cai -S pr_file_name=PR.md # optional: change the filename
353
+ git cai -r # writes ./PR.md (or PR_DESCRIPTION.md by default)
354
+ ```
355
+
356
+ Configuration follows the usual precedence: repo `cai_config.yml` wins,
357
+ otherwise `~/.config/cai/cai_config.yml`, otherwise the built-in defaults.
358
+
320
359
  ### Changing configuration from the CLI
321
360
 
322
361
  Instead of editing YAML files manually, use `--set` or `--set-home` to update config values.
@@ -52,6 +52,7 @@ Currently supported providers:
52
52
  - Conventional Commits format support
53
53
  - Change configuration from the command line
54
54
  - Optional commit squashing with automatic summary generation (all, last N, or up to a specific commit)
55
+ - Pull Request description generator (`--PR`) that summarizes the commits between the current branch and its base
55
56
  - List providers, models, active config, and file paths
56
57
  - Token usage logging for API calls
57
58
  - Branch name as LLM context
@@ -204,6 +205,9 @@ git cai -g
204
205
  - `conventional` – use Conventional Commits format
205
206
  - `token_logging` – log token usage after each LLM call
206
207
  - `measure_time` – log generation time
208
+ - `pr_to_file` – when `--PR` is used, write the generated description to a Markdown file in the repo root instead of stdout (default `false`)
209
+ - `pr_file_name` – filename used when `pr_to_file` is `true` (default `PR_DESCRIPTION.md`)
210
+ - `pr_prompt_file` – optional path to a custom Markdown prompt for `--PR` (falls back to `~/.config/cai/pr_prompt.md`, then a built-in default)
207
211
 
208
212
  ---
209
213
 
@@ -227,6 +231,8 @@ In addition to `git cai`, the following options are available:
227
231
  - `-m`, `--model` – override the model for this invocation (requires `-P`)
228
232
  - `-p`, `--generate-prompts` – generate default `commit_prompt.md` and `squash_prompt.md` in the current directory (for customization)
229
233
  - `-P`, `--provider` – override the LLM provider for this invocation
234
+ - `-r`, `--PR` – generate a Pull Request description from the commits between the current branch and its base (prints to stdout by default; set `pr_to_file=true` to write a Markdown file)
235
+ - `--base` `BRANCH` – explicit base branch for `--PR` (overrides auto-detection: `origin/HEAD` → `main` → `master`)
230
236
  - `-S`, `--set` – set a config value (`key=value`) in repo config (requires existing repo config)
231
237
  - `-s`, `--squash` `[N|HASH]` – squash commits on the current branch and summarize them. Without argument: squash all since branch checkout. With a number: squash the last N commits. With a commit hash: squash up to and including that commit
232
238
  - `-T`, `--timeout` `SECONDS` – HTTP timeout for this invocation (overrides config)
@@ -288,6 +294,39 @@ Persist the default:
288
294
  git cai -S full_files=true
289
295
  ```
290
296
 
297
+ ### Pull Request descriptions
298
+
299
+ `-r` / `--PR` generates a Markdown Pull Request description from the commits
300
+ between the current branch and its base branch. The output has two sections —
301
+ `## Summary` (bullet list following the same best practices as commit messages:
302
+ imperative mood, capitalized, no trailing period, 72-char wrap) and
303
+ `## Test plan` (a checklist a reviewer can run).
304
+
305
+ This mode never modifies git state — no commit, no reset, no force push.
306
+
307
+ ```sh
308
+ git cai -r # print to stdout (default)
309
+ git cai --PR # long form
310
+ git cai -r --base develop # explicit base branch
311
+ git cai -r -x "Closes JIRA-1234" # add extra context
312
+ ```
313
+
314
+ The base branch is auto-detected in this order: `origin/HEAD`, then local
315
+ `main`, then local `master`. Use `--base` for repositories with non-standard
316
+ layouts or no `origin/HEAD`.
317
+
318
+ By default the description is printed to stdout. To write it to a file in the
319
+ repository root instead, set `pr_to_file: true`:
320
+
321
+ ```sh
322
+ git cai -S pr_to_file=true
323
+ git cai -S pr_file_name=PR.md # optional: change the filename
324
+ git cai -r # writes ./PR.md (or PR_DESCRIPTION.md by default)
325
+ ```
326
+
327
+ Configuration follows the usual precedence: repo `cai_config.yml` wins,
328
+ otherwise `~/.config/cai/cai_config.yml`, otherwise the built-in defaults.
329
+
291
330
  ### Changing configuration from the CLI
292
331
 
293
332
  Instead of editing YAML files manually, use `--set` or `--set-home` to update config values.
@@ -12,6 +12,9 @@ token_logging: true
12
12
  measure_time: true
13
13
  timeout: 30
14
14
  full_files: false
15
+ pr_to_file: false
16
+ pr_file_name: PR_DESCRIPTION.md
17
+ pr_prompt_file: "/home/thorsten/.config/cai/pr_prompt.md"
15
18
  anthropic:
16
19
  model: claude-haiku-4-5
17
20
  temperature: 0
@@ -20,6 +20,7 @@ SYNOPSIS
20
20
  --list [config|editor|language|model|path|provider|style]]
21
21
  [-m MODEL | --model MODEL]
22
22
  [-P PROVIDER | --provider PROVIDER] [-p | --generate-prompts]
23
+ [-r | --PR] [--base BRANCH]
23
24
  [-S KEY=VALUE | --set KEY=VALUE]
24
25
  [-s [N|HASH] | --squash [N|HASH]]
25
26
  [-T SECONDS | --timeout SECONDS]
@@ -299,6 +300,48 @@ overwrites.
299
300
  git cai -p
300
301
  ----
301
302
 
303
+ -r, --PR::
304
+ Generate a Pull Request description summarizing the commits between the
305
+ current branch and its base branch. git-cai collects the commit messages
306
+ and the list of files changed since the branch diverged, sends them to
307
+ the configured LLM, and produces a Markdown description with `## Summary`
308
+ and `## Test plan` sections.
309
+ +
310
+ This mode never modifies git state: no commit, no reset, no force push.
311
+ +
312
+ By default the generated description is printed to stdout. To write it to
313
+ a file in the repository root instead, set `pr_to_file: true` in
314
+ `cai_config.yml`. The filename defaults to `PR_DESCRIPTION.md` and can be
315
+ changed via `pr_file_name`. Configuration follows the same precedence as
316
+ every other key (repository config wins; otherwise home config; otherwise
317
+ built-in defaults).
318
+ +
319
+ The base branch is auto-detected in this order: `origin/HEAD`, then local
320
+ `main`, then local `master`. Use `--base` to override.
321
+ +
322
+ Cannot be combined with `--amend`, `--list`, `--squash`, or `--update`.
323
+ Can be combined with `--context` to inject extra context (e.g. a ticket
324
+ number or reviewer ask).
325
+ +
326
+ ----
327
+ git cai -r
328
+ git cai --PR
329
+ git cai -r --base develop
330
+ git cai -r -x "Closes JIRA-1234"
331
+ ----
332
+
333
+ --base BRANCH::
334
+ Explicit base branch for `--PR`. Overrides the auto-detection chain
335
+ (`origin/HEAD` -> `main` -> `master`). Useful for repositories whose base
336
+ branch is not `main`/`master` or that lack `origin/HEAD`.
337
+ +
338
+ Only meaningful in combination with `--PR` (`-r`).
339
+ +
340
+ ----
341
+ git cai -r --base develop
342
+ git cai -r --base release/1.x
343
+ ----
344
+
302
345
  -S, --set KEY=VALUE::
303
346
  Set a configuration value in the repository config (`cai_config.yml` in the
304
347
  repository root). Requires an existing repo config; if none is found, an
@@ -399,12 +442,13 @@ git cai -v
399
442
 
400
443
  -x, --context CONTEXT::
401
444
  Provide extra context for the LLM to consider when generating the commit
402
- message. The context string is appended to the diff (or commit history in squash
403
- mode) before sending it to the provider. This is useful for including information
404
- that is not visible in the diff itself, such as a ticket number, the reason for
405
- a change, or a link to an issue.
445
+ message. The context string is appended to the diff (or commit history in
446
+ squash mode, or the commit list in PR mode) before sending it to the
447
+ provider. This is useful for including information that is not visible in
448
+ the diff itself, such as a ticket number, the reason for a change, or a
449
+ link to an issue.
406
450
  +
407
- Can be combined with any commit-generating mode (`COMMIT`, `AMEND`, or `SQUASH`).
451
+ Can be combined with `COMMIT`, `AMEND`, `SQUASH`, or `PR` modes.
408
452
  Cannot be used with `--list` or `--update`.
409
453
  +
410
454
  ----
@@ -412,6 +456,7 @@ git cai -x "Fixes JIRA-1234"
412
456
  git cai -x "Performance optimisation for the search endpoint"
413
457
  git cai -A -x "Reword after code review feedback"
414
458
  git cai --squash -x "Resolves JIRA-99"
459
+ git cai -r -x "Closes JIRA-1234"
415
460
  ----
416
461
 
417
462
  LIST TYPES
@@ -622,6 +667,16 @@ Available configuration keys:
622
667
  - `timeout` -- HTTP timeout in seconds for remote LLM calls (default `30`)
623
668
  - `full_files` -- send full working-tree contents of staged files alongside
624
669
  the diff (`true`/`false`, default `false`)
670
+ - `pr_to_file` -- when running `--PR`, write the generated PR description
671
+ to a Markdown file in the repository root instead of printing it to
672
+ stdout (`true`/`false`, default `false`)
673
+ - `pr_file_name` -- filename used when `pr_to_file` is `true` (default
674
+ `PR_DESCRIPTION.md`). The file is written in the repository root and
675
+ overwritten on each run.
676
+ - `pr_prompt_file` -- path to a custom Markdown prompt file used by
677
+ `--PR`. Follows the same fallback chain as `prompt_file` /
678
+ `squash_prompt_file`: configured path -> `~/.config/cai/pr_prompt.md` ->
679
+ built-in fallback.
625
680
  - `<provider>.model` -- model name for a specific provider
626
681
  - `<provider>.temperature` -- temperature for a specific provider
627
682
  - `anthropic.max_tokens` -- upper bound on Anthropic response tokens
@@ -720,6 +775,35 @@ Squash all commits down to and including a specific commit:
720
775
  git cai -s a1b2c3d
721
776
  ----
722
777
 
778
+ Generate a Pull Request description from the current branch (prints to
779
+ stdout):
780
+
781
+ ----
782
+ git cai -r
783
+ git cai --PR
784
+ ----
785
+
786
+ Generate a PR description against an explicit base branch:
787
+
788
+ ----
789
+ git cai -r --base develop
790
+ ----
791
+
792
+ Write the PR description to a file in the repository root (set once in
793
+ `cai_config.yml`, then run normally):
794
+
795
+ ----
796
+ git cai -S pr_to_file=true
797
+ git cai -S pr_file_name=PR.md
798
+ git cai -r
799
+ ----
800
+
801
+ Generate a PR description with extra context:
802
+
803
+ ----
804
+ git cai -r -x "Closes JIRA-1234"
805
+ ----
806
+
723
807
  Show supported styles, languages, or editors:
724
808
 
725
809
  ----
@@ -2,12 +2,12 @@
2
2
  .\" Title: git-cai
3
3
  .\" Author: Thorsten Foltz
4
4
  .\" Generator: Asciidoctor 2.0.26
5
- .\" Date: 2026-04-23
5
+ .\" Date: 2026-04-27
6
6
  .\" Manual: \ \&
7
7
  .\" Source: \ \&
8
8
  .\" Language: English
9
9
  .\"
10
- .TH "GIT\-CAI" "1" "2026-04-23" "\ \&" "\ \&"
10
+ .TH "GIT\-CAI" "1" "2026-04-27" "\ \&" "\ \&"
11
11
  .ie \n(.g .ds Aq \(aq
12
12
  .el .ds Aq '
13
13
  .ss \n[.ss] 0
@@ -42,6 +42,7 @@ git-cai \- AI\-powered commit message generator
42
42
  \-\-list [config|editor|language|model|path|provider|style]]
43
43
  [\-m MODEL | \-\-model MODEL]
44
44
  [\-P PROVIDER | \-\-provider PROVIDER] [\-p | \-\-generate\-prompts]
45
+ [\-r | \-\-PR] [\-\-base BRANCH]
45
46
  [\-S KEY=VALUE | \-\-set KEY=VALUE]
46
47
  [\-s [N|HASH] | \-\-squash [N|HASH]]
47
48
  [\-T SECONDS | \-\-timeout SECONDS]
@@ -523,6 +524,60 @@ git cai \-p
523
524
  .if n .RE
524
525
  .RE
525
526
  .sp
527
+ \-r, \-\-PR
528
+ .RS 4
529
+ Generate a Pull Request description summarizing the commits between the
530
+ current branch and its base branch. git\-cai collects the commit messages
531
+ and the list of files changed since the branch diverged, sends them to
532
+ the configured LLM, and produces a Markdown description with \f(CR## Summary\fP
533
+ and \f(CR## Test plan\fP sections.
534
+ .sp
535
+ This mode never modifies git state: no commit, no reset, no force push.
536
+ .sp
537
+ By default the generated description is printed to stdout. To write it to
538
+ a file in the repository root instead, set \f(CRpr_to_file: true\fP in
539
+ \f(CRcai_config.yml\fP. The filename defaults to \f(CRPR_DESCRIPTION.md\fP and can be
540
+ changed via \f(CRpr_file_name\fP. Configuration follows the same precedence as
541
+ every other key (repository config wins; otherwise home config; otherwise
542
+ built\-in defaults).
543
+ .sp
544
+ The base branch is auto\-detected in this order: \f(CRorigin/HEAD\fP, then local
545
+ \f(CRmain\fP, then local \f(CRmaster\fP. Use \f(CR\-\-base\fP to override.
546
+ .sp
547
+ Cannot be combined with \f(CR\-\-amend\fP, \f(CR\-\-list\fP, \f(CR\-\-squash\fP, or \f(CR\-\-update\fP.
548
+ Can be combined with \f(CR\-\-context\fP to inject extra context (e.g. a ticket
549
+ number or reviewer ask).
550
+ .sp
551
+ .if n .RS 4
552
+ .nf
553
+ .fam C
554
+ git cai \-r
555
+ git cai \-\-PR
556
+ git cai \-r \-\-base develop
557
+ git cai \-r \-x "Closes JIRA\-1234"
558
+ .fam
559
+ .fi
560
+ .if n .RE
561
+ .RE
562
+ .sp
563
+ \-\-base BRANCH
564
+ .RS 4
565
+ Explicit base branch for \f(CR\-\-PR\fP. Overrides the auto\-detection chain
566
+ (\f(CRorigin/HEAD\fP \(-> \f(CRmain\fP \(-> \f(CRmaster\fP). Useful for repositories whose base
567
+ branch is not \f(CRmain\fP/\f(CRmaster\fP or that lack \f(CRorigin/HEAD\fP.
568
+ .sp
569
+ Only meaningful in combination with \f(CR\-\-PR\fP (\f(CR\-r\fP).
570
+ .sp
571
+ .if n .RS 4
572
+ .nf
573
+ .fam C
574
+ git cai \-r \-\-base develop
575
+ git cai \-r \-\-base release/1.x
576
+ .fam
577
+ .fi
578
+ .if n .RE
579
+ .RE
580
+ .sp
526
581
  \-S, \-\-set KEY=VALUE
527
582
  .RS 4
528
583
  Set a configuration value in the repository config (\f(CRcai_config.yml\fP in the
@@ -668,12 +723,13 @@ git cai \-v
668
723
  \-x, \-\-context CONTEXT
669
724
  .RS 4
670
725
  Provide extra context for the LLM to consider when generating the commit
671
- message. The context string is appended to the diff (or commit history in squash
672
- mode) before sending it to the provider. This is useful for including information
673
- that is not visible in the diff itself, such as a ticket number, the reason for
674
- a change, or a link to an issue.
726
+ message. The context string is appended to the diff (or commit history in
727
+ squash mode, or the commit list in PR mode) before sending it to the
728
+ provider. This is useful for including information that is not visible in
729
+ the diff itself, such as a ticket number, the reason for a change, or a
730
+ link to an issue.
675
731
  .sp
676
- Can be combined with any commit\-generating mode (\f(CRCOMMIT\fP, \f(CRAMEND\fP, or \f(CRSQUASH\fP).
732
+ Can be combined with \f(CRCOMMIT\fP, \f(CRAMEND\fP, \f(CRSQUASH\fP, or \f(CRPR\fP modes.
677
733
  Cannot be used with \f(CR\-\-list\fP or \f(CR\-\-update\fP.
678
734
  .sp
679
735
  .if n .RS 4
@@ -683,6 +739,7 @@ git cai \-x "Fixes JIRA\-1234"
683
739
  git cai \-x "Performance optimisation for the search endpoint"
684
740
  git cai \-A \-x "Reword after code review feedback"
685
741
  git cai \-\-squash \-x "Resolves JIRA\-99"
742
+ git cai \-r \-x "Closes JIRA\-1234"
686
743
  .fam
687
744
  .fi
688
745
  .if n .RE
@@ -1131,6 +1188,46 @@ the diff (\f(CRtrue\fP/\f(CRfalse\fP, default \f(CRfalse\fP)
1131
1188
  . sp -1
1132
1189
  . IP \(bu 2.3
1133
1190
  .\}
1191
+ \f(CRpr_to_file\fP \(em when running \f(CR\-\-PR\fP, write the generated PR description
1192
+ to a Markdown file in the repository root instead of printing it to
1193
+ stdout (\f(CRtrue\fP/\f(CRfalse\fP, default \f(CRfalse\fP)
1194
+ .RE
1195
+ .sp
1196
+ .RS 4
1197
+ .ie n \{\
1198
+ \h'-04'\(bu\h'+03'\c
1199
+ .\}
1200
+ .el \{\
1201
+ . sp -1
1202
+ . IP \(bu 2.3
1203
+ .\}
1204
+ \f(CRpr_file_name\fP \(em filename used when \f(CRpr_to_file\fP is \f(CRtrue\fP (default
1205
+ \f(CRPR_DESCRIPTION.md\fP). The file is written in the repository root and
1206
+ overwritten on each run.
1207
+ .RE
1208
+ .sp
1209
+ .RS 4
1210
+ .ie n \{\
1211
+ \h'-04'\(bu\h'+03'\c
1212
+ .\}
1213
+ .el \{\
1214
+ . sp -1
1215
+ . IP \(bu 2.3
1216
+ .\}
1217
+ \f(CRpr_prompt_file\fP \(em path to a custom Markdown prompt file used by
1218
+ \f(CR\-\-PR\fP. Follows the same fallback chain as \f(CRprompt_file\fP /
1219
+ \f(CRsquash_prompt_file\fP: configured path \(-> \f(CR~/.config/cai/pr_prompt.md\fP \(->
1220
+ built\-in fallback.
1221
+ .RE
1222
+ .sp
1223
+ .RS 4
1224
+ .ie n \{\
1225
+ \h'-04'\(bu\h'+03'\c
1226
+ .\}
1227
+ .el \{\
1228
+ . sp -1
1229
+ . IP \(bu 2.3
1230
+ .\}
1134
1231
  \f(CR<provider>.model\fP \(em model name for a specific provider
1135
1232
  .RE
1136
1233
  .sp
@@ -1322,6 +1419,51 @@ git cai \-s a1b2c3d
1322
1419
  .fi
1323
1420
  .if n .RE
1324
1421
  .sp
1422
+ Generate a Pull Request description from the current branch (prints to
1423
+ stdout):
1424
+ .sp
1425
+ .if n .RS 4
1426
+ .nf
1427
+ .fam C
1428
+ git cai \-r
1429
+ git cai \-\-PR
1430
+ .fam
1431
+ .fi
1432
+ .if n .RE
1433
+ .sp
1434
+ Generate a PR description against an explicit base branch:
1435
+ .sp
1436
+ .if n .RS 4
1437
+ .nf
1438
+ .fam C
1439
+ git cai \-r \-\-base develop
1440
+ .fam
1441
+ .fi
1442
+ .if n .RE
1443
+ .sp
1444
+ Write the PR description to a file in the repository root (set once in
1445
+ \f(CRcai_config.yml\fP, then run normally):
1446
+ .sp
1447
+ .if n .RS 4
1448
+ .nf
1449
+ .fam C
1450
+ git cai \-S pr_to_file=true
1451
+ git cai \-S pr_file_name=PR.md
1452
+ git cai \-r
1453
+ .fam
1454
+ .fi
1455
+ .if n .RE
1456
+ .sp
1457
+ Generate a PR description with extra context:
1458
+ .sp
1459
+ .if n .RS 4
1460
+ .nf
1461
+ .fam C
1462
+ git cai \-r \-x "Closes JIRA\-1234"
1463
+ .fam
1464
+ .fi
1465
+ .if n .RE
1466
+ .sp
1325
1467
  Show supported styles, languages, or editors:
1326
1468
  .sp
1327
1469
  .if n .RS 4
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '0.12.2'
22
- __version_tuple__ = version_tuple = (0, 12, 2)
21
+ __version__ = version = '0.13.1'
22
+ __version_tuple__ = version_tuple = (0, 13, 1)
23
23
 
24
- __commit_id__ = commit_id = 'gf511d4825'
24
+ __commit_id__ = commit_id = 'gab0a91cf3'
@@ -78,6 +78,17 @@ def callback( # pylint: disable=too-many-arguments,too-many-positional-argument
78
78
  squash: bool = typer.Option(
79
79
  False, "--squash", "-s", help="Squash commits on this branch"
80
80
  ),
81
+ pr: bool = typer.Option(
82
+ False,
83
+ "--PR",
84
+ "-r",
85
+ help="Generate a Pull Request description from the commits on this branch",
86
+ ),
87
+ base: str = typer.Option(
88
+ None,
89
+ "--base",
90
+ help="Explicit base branch for --PR (overrides auto-detection)",
91
+ ),
81
92
  update: bool = typer.Option(False, "--update", "-u", help="Check for updates"),
82
93
  set_config: str = typer.Option(
83
94
  None,
@@ -143,7 +154,13 @@ def callback( # pylint: disable=too-many-arguments,too-many-positional-argument
143
154
  do_install()
144
155
  raise typer.Exit()
145
156
 
146
- mode = resolve_mode(amend=amend, list_flag=list_flag, squash=squash, update=update)
157
+ mode = resolve_mode(
158
+ amend=amend,
159
+ list_flag=list_flag,
160
+ pr=pr,
161
+ squash=squash,
162
+ update=update,
163
+ )
147
164
 
148
165
  validate_options(
149
166
  mode=mode,
@@ -180,8 +197,8 @@ def callback( # pylint: disable=too-many-arguments,too-many-positional-argument
180
197
  try:
181
198
  manager.generate_prompts_here()
182
199
  typer.echo(
183
- "commit_prompt.md, squash_prompt.md, and full_files_prompt.md "
184
- "created in current directory."
200
+ "commit_prompt.md, squash_prompt.md, full_files_prompt.md, "
201
+ "and pr_prompt.md created in current directory."
185
202
  )
186
203
  except RuntimeError as e:
187
204
  typer.echo(f"Error: {e}", err=True)
@@ -228,6 +245,7 @@ def callback( # pylint: disable=too-many-arguments,too-many-positional-argument
228
245
  timeout_override=timeout,
229
246
  full_files_override=full_files,
230
247
  files_override=files,
248
+ base_override=base,
231
249
  )
232
250
 
233
251
 
@@ -30,7 +30,9 @@ Flags:
30
30
  -l, --list List information (e.g. languages, styles, providers, config)
31
31
  -m, --model NAME Override model for this invocation (requires --provider)
32
32
  -P, --provider NAME Override LLM provider for this invocation
33
- -p, --generate-prompts Generate default commit_prompt.md and squash_prompt.md
33
+ -p, --generate-prompts Generate default commit/squash/full_files/pr prompt files
34
+ -r, --PR Generate a Pull Request description from the commits on this branch
35
+ --base BRANCH Base branch for --PR (overrides auto-detection)
34
36
  -S, --set KEY=VALUE Set a config value in repo config (requires existing repo config)
35
37
  -s, --squash [N|HASH] Squash commits on this branch and summarize them
36
38
  No argument: squash all commits since branch checkout
@@ -15,18 +15,27 @@ class Mode(Enum):
15
15
  AMEND = auto()
16
16
  COMMIT = auto()
17
17
  LIST = auto()
18
+ PR = auto()
18
19
  SQUASH = auto()
19
20
  UPDATE = auto()
20
21
 
21
22
 
22
- def resolve_mode(*, amend: bool, list_flag: bool, squash: bool, update: bool) -> Mode:
23
+ def resolve_mode(
24
+ *,
25
+ amend: bool,
26
+ list_flag: bool,
27
+ pr: bool,
28
+ squash: bool,
29
+ update: bool,
30
+ ) -> Mode:
23
31
  """
24
32
  Resolves the operational mode based on the provided flags.
25
33
  """
26
- flags = [amend, list_flag, squash, update]
34
+ flags = [amend, list_flag, pr, squash, update]
27
35
  if sum(flags) > 1:
28
36
  typer.echo(
29
- "Error: --amend, --list, --squash, and --update cannot be used together.",
37
+ "Error: --amend, --list, --PR, --squash, and --update "
38
+ "cannot be used together.",
30
39
  err=True,
31
40
  )
32
41
  raise typer.Exit(code=1)
@@ -35,6 +44,8 @@ def resolve_mode(*, amend: bool, list_flag: bool, squash: bool, update: bool) ->
35
44
  return Mode.AMEND
36
45
  if list_flag:
37
46
  return Mode.LIST
47
+ if pr:
48
+ return Mode.PR
38
49
  if squash:
39
50
  return Mode.SQUASH
40
51
  if update:
@@ -68,7 +79,7 @@ def validate_options(
68
79
 
69
80
  if stage_tracked and mode not in (Mode.COMMIT, Mode.AMEND):
70
81
  typer.echo(
71
- "Error: --all cannot be used with --list, --update, or --squash.",
82
+ "Error: --all cannot be used with --list, --update, --PR, or --squash.",
72
83
  err=True,
73
84
  )
74
85
  raise typer.Exit(code=1)
@@ -87,7 +98,7 @@ def validate_options(
87
98
  )
88
99
  raise typer.Exit(code=1)
89
100
 
90
- if context and mode not in (Mode.COMMIT, Mode.AMEND, Mode.SQUASH):
101
+ if context and mode not in (Mode.COMMIT, Mode.AMEND, Mode.SQUASH, Mode.PR):
91
102
  typer.echo(
92
103
  "Error: --context cannot be used with --list or --update.",
93
104
  err=True,
@@ -96,7 +107,7 @@ def validate_options(
96
107
 
97
108
  if files and mode not in (Mode.COMMIT, Mode.AMEND):
98
109
  typer.echo(
99
- "Error: --files cannot be used with --list, --update, or --squash.",
110
+ "Error: --files cannot be used with --list, --update, --PR, or --squash.",
100
111
  err=True,
101
112
  )
102
113
  raise typer.Exit(code=1)