git-cai-cli 0.14.1__tar.gz → 0.14.2__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 (97) hide show
  1. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/PKG-INFO +5 -5
  2. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/README.md +4 -4
  3. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/docs/man/git-cai.1 +2 -2
  4. git_cai_cli-0.14.2/docs/superpowers/specs/2026-06-14-custom-styles-design.md +203 -0
  5. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/src/git_cai_cli/_version.py +3 -3
  6. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/src/git_cai_cli/cli/cli.py +4 -1
  7. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/src/git_cai_cli/cli/helptext.py +4 -0
  8. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/src/git_cai_cli/core/prompts_fallback.py +2 -2
  9. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/src/git_cai_cli.egg-info/PKG-INFO +5 -5
  10. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/src/git_cai_cli.egg-info/SOURCES.txt +1 -0
  11. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/tests/unit/test_prompt_loading.py +25 -0
  12. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/.caiignore +0 -0
  13. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/.gitattributes +0 -0
  14. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/.github/cd/.SRCINFO +0 -0
  15. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/.github/cd/PKGBUILD +0 -0
  16. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/.github/ci/_version.py +0 -0
  17. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/.github/ci/cai_config.ci.yml +0 -0
  18. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/.github/ci/tokens.ci.yml +0 -0
  19. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/.github/workflows/python-tests.yml +0 -0
  20. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/.github/workflows/release.yml +0 -0
  21. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/.github/workflows/release_aur.yml +0 -0
  22. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/.gitignore +0 -0
  23. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/.linters/.bandit.yml +0 -0
  24. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/.linters/.checkov.yml +0 -0
  25. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/.linters/.flake8 +0 -0
  26. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/.linters/.ls-lint.yml +0 -0
  27. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/.linters/.markdown-link-check.json +0 -0
  28. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/.linters/.markdownlint.json +0 -0
  29. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/.linters/.proselintrc +0 -0
  30. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/.linters/.pylintrc +0 -0
  31. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/.linters/.yamllint.yml +0 -0
  32. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/.linters/check_git_branch_name.sh +0 -0
  33. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/.linters/lychee.toml +0 -0
  34. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/.linters/pyrightconfig.json +0 -0
  35. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/.markdownlintignore +0 -0
  36. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/.mega-linter.yml +0 -0
  37. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/.semgrepignore +0 -0
  38. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/.trivyignore +0 -0
  39. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/CLAUDE.md +0 -0
  40. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/LICENSE +0 -0
  41. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/Makefile +0 -0
  42. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/cai_config.yml +0 -0
  43. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/docs/git-cai.txt +0 -0
  44. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/pyproject.toml +0 -0
  45. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/setup.cfg +0 -0
  46. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/src/git_cai_cli/__init__.py +0 -0
  47. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/src/git_cai_cli/cli/__init__.py +0 -0
  48. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/src/git_cai_cli/cli/modes.py +0 -0
  49. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/src/git_cai_cli/core/__init__.py +0 -0
  50. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/src/git_cai_cli/core/completion.py +0 -0
  51. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/src/git_cai_cli/core/config.py +0 -0
  52. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/src/git_cai_cli/core/editors.py +0 -0
  53. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/src/git_cai_cli/core/gitutils.py +0 -0
  54. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/src/git_cai_cli/core/init.py +0 -0
  55. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/src/git_cai_cli/core/languages.py +0 -0
  56. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/src/git_cai_cli/core/llm.py +0 -0
  57. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/src/git_cai_cli/core/options.py +0 -0
  58. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/src/git_cai_cli/core/pr.py +0 -0
  59. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/src/git_cai_cli/core/spinner.py +0 -0
  60. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/src/git_cai_cli/core/squash.py +0 -0
  61. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/src/git_cai_cli/core/stats.py +0 -0
  62. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/src/git_cai_cli/core/validate.py +0 -0
  63. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/src/git_cai_cli/main.py +0 -0
  64. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/src/git_cai_cli.egg-info/dependency_links.txt +0 -0
  65. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/src/git_cai_cli.egg-info/entry_points.txt +0 -0
  66. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/src/git_cai_cli.egg-info/requires.txt +0 -0
  67. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/src/git_cai_cli.egg-info/top_level.txt +0 -0
  68. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/tests/conftest.py +0 -0
  69. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/tests/integration/test_cli_integration.py +0 -0
  70. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/tests/integration/test_config_integration.py +0 -0
  71. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/tests/integration/test_gitutils_integration.py +0 -0
  72. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/tests/integration/test_modes_integration.py +0 -0
  73. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/tests/integration/test_options_integration.py +0 -0
  74. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/tests/integration/test_pr_integration.py +0 -0
  75. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/tests/integration/test_squash_integration.py +0 -0
  76. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/tests/unit/test_amend.py +0 -0
  77. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/tests/unit/test_branch_context.py +0 -0
  78. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/tests/unit/test_cli.py +0 -0
  79. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/tests/unit/test_completion.py +0 -0
  80. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/tests/unit/test_config.py +0 -0
  81. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/tests/unit/test_conventional.py +0 -0
  82. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/tests/unit/test_gitutils.py +0 -0
  83. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/tests/unit/test_helptext.py +0 -0
  84. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/tests/unit/test_init.py +0 -0
  85. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/tests/unit/test_llm.py +0 -0
  86. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/tests/unit/test_main.py +0 -0
  87. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/tests/unit/test_modes.py +0 -0
  88. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/tests/unit/test_options.py +0 -0
  89. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/tests/unit/test_pr.py +0 -0
  90. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/tests/unit/test_print.py +0 -0
  91. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/tests/unit/test_set_config.py +0 -0
  92. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/tests/unit/test_signoff.py +0 -0
  93. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/tests/unit/test_spinner.py +0 -0
  94. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/tests/unit/test_squash.py +0 -0
  95. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/tests/unit/test_stats.py +0 -0
  96. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/tests/unit/test_validate.py +0 -0
  97. {git_cai_cli-0.14.1 → git_cai_cli-0.14.2}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: git-cai-cli
3
- Version: 0.14.1
3
+ Version: 0.14.2
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
@@ -274,7 +274,7 @@ In addition to `git cai`, the following options are available:
274
274
  - `-C`, `--conventional` – use Conventional Commits format (`type(scope): description`)
275
275
  - `-c`, `--crazy` – Trust the LLM and commit without checking
276
276
  - `-d`, `--debug` – enable debug logging
277
- - `-e`, `--temperature` `TEMPERATURE` – override the active provider's sampling temperature for this invocation (provider-scoped, like `-m`)
277
+ - `-E`, `--temperature` `TEMPERATURE` – override the active provider's sampling temperature for this invocation (provider scoped, like `-m`)
278
278
  - `-F`, `--full-files` – attach the full contents of affected files alongside the diff (uses `full_files_prompt.md`)
279
279
  - `-f`, `--files` `PATH` – limit the diff (and full-file content, if enabled) to PATH; repeat for multiple files
280
280
  - `-g`, `--generate-config` – generate the default `cai_config.yml` in the current directory
@@ -297,9 +297,9 @@ In addition to `git cai`, the following options are available:
297
297
  - `--base` `BRANCH` – explicit base branch for `--PR` (overrides auto-detection: `origin/HEAD` → `main` → `master`)
298
298
  - `-S`, `--set` – set a config value (`key=value`) in repo config (requires existing repo config)
299
299
  - `-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
300
- - `--style` `STYLE` – override the commit message style for this invocation (e.g. `funny`, `neutral`, `none`); validated against the supported styles
301
- - `--language` `CODE` – override the commit message language for this invocation (e.g. `de`, `fr`, `none`); validated against supported codes
302
- - `--emoji` / `--no-emoji` – override emoji usage for this invocation (use `--no-emoji` to disable when config enables it)
300
+ - `-y`, `--style` `STYLE` – override the commit message style for this invocation (e.g. `funny`, `neutral`, `none`); validated against the supported styles
301
+ - `-L`, `--language` `CODE` – override the commit message language for this invocation (e.g. `de`, `fr`, `none`); validated against supported codes
302
+ - `-e`, `--emoji` / `--no-emoji` – override emoji usage for this invocation (use `--no-emoji` to disable when config enables it)
303
303
  - `-T`, `--timeout` `SECONDS` – HTTP timeout for this invocation (overrides config)
304
304
  - `-t`, `--time` – measure and log commit message generation time
305
305
  - `-u`, `--update` – check for updates
@@ -244,7 +244,7 @@ In addition to `git cai`, the following options are available:
244
244
  - `-C`, `--conventional` – use Conventional Commits format (`type(scope): description`)
245
245
  - `-c`, `--crazy` – Trust the LLM and commit without checking
246
246
  - `-d`, `--debug` – enable debug logging
247
- - `-e`, `--temperature` `TEMPERATURE` – override the active provider's sampling temperature for this invocation (provider-scoped, like `-m`)
247
+ - `-E`, `--temperature` `TEMPERATURE` – override the active provider's sampling temperature for this invocation (provider scoped, like `-m`)
248
248
  - `-F`, `--full-files` – attach the full contents of affected files alongside the diff (uses `full_files_prompt.md`)
249
249
  - `-f`, `--files` `PATH` – limit the diff (and full-file content, if enabled) to PATH; repeat for multiple files
250
250
  - `-g`, `--generate-config` – generate the default `cai_config.yml` in the current directory
@@ -267,9 +267,9 @@ In addition to `git cai`, the following options are available:
267
267
  - `--base` `BRANCH` – explicit base branch for `--PR` (overrides auto-detection: `origin/HEAD` → `main` → `master`)
268
268
  - `-S`, `--set` – set a config value (`key=value`) in repo config (requires existing repo config)
269
269
  - `-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
270
- - `--style` `STYLE` – override the commit message style for this invocation (e.g. `funny`, `neutral`, `none`); validated against the supported styles
271
- - `--language` `CODE` – override the commit message language for this invocation (e.g. `de`, `fr`, `none`); validated against supported codes
272
- - `--emoji` / `--no-emoji` – override emoji usage for this invocation (use `--no-emoji` to disable when config enables it)
270
+ - `-y`, `--style` `STYLE` – override the commit message style for this invocation (e.g. `funny`, `neutral`, `none`); validated against the supported styles
271
+ - `-L`, `--language` `CODE` – override the commit message language for this invocation (e.g. `de`, `fr`, `none`); validated against supported codes
272
+ - `-e`, `--emoji` / `--no-emoji` – override emoji usage for this invocation (use `--no-emoji` to disable when config enables it)
273
273
  - `-T`, `--timeout` `SECONDS` – HTTP timeout for this invocation (overrides config)
274
274
  - `-t`, `--time` – measure and log commit message generation time
275
275
  - `-u`, `--update` – check for updates
@@ -2,12 +2,12 @@
2
2
  .\" Title: git-cai
3
3
  .\" Author: Thorsten Foltz
4
4
  .\" Generator: Asciidoctor 2.0.26
5
- .\" Date: 2026-06-04
5
+ .\" Date: 2026-06-14
6
6
  .\" Manual: \ \&
7
7
  .\" Source: \ \&
8
8
  .\" Language: English
9
9
  .\"
10
- .TH "GIT\-CAI" "1" "2026-06-04" "\ \&" "\ \&"
10
+ .TH "GIT\-CAI" "1" "2026-06-14" "\ \&" "\ \&"
11
11
  .ie \n(.g .ds Aq \(aq
12
12
  .el .ds Aq '
13
13
  .ss \n[.ss] 0
@@ -0,0 +1,203 @@
1
+ # Custom styles + richer built-in style guidance — Design
2
+
3
+ **Date:** 2026-06-14
4
+ **Branch:** `fix/style-option`
5
+ **Status:** Approved
6
+
7
+ ## Goal
8
+
9
+ Extend the commit-message `style` option in two ways:
10
+
11
+ - **A — User-defined custom styles:** let users define their own styles in
12
+ `cai_config.yml` via a `custom_styles` map, so individuals and teams can add
13
+ tones beyond the shipped set without code changes.
14
+ - **C — Richer built-in style guidance:** replace the single generic style
15
+ instruction with a per-style prompt fragment, so each style (built-in or
16
+ custom) contributes real tone guidance to the prompt.
17
+ - **E — Single registry (cleanup):** consolidate the built-in style data, which
18
+ today is split between the allowlist in `validate.py` and the
19
+ descriptions/examples in `options.py::styles()`, into one source of truth.
20
+ - **F — Override flag ergonomics:** give the invocation-override options short
21
+ flags and make them visible in `git cai -h`. Today `--style`, `--language`,
22
+ `--emoji`, and `--temperature` are long-only **and** absent from the
23
+ hand-maintained help text (`cli/helptext.py`).
24
+
25
+ ## Background (current behaviour)
26
+
27
+ - `_validate_style()` (`core/validate.py`) checks a hardcoded allowlist:
28
+ `professional, neutral, friendly, funny, excited, sarcastic, apologetic,
29
+ academic`, plus `none` (disables the style instruction).
30
+ - `_style_instruction()` (`core/llm.py`) emits one generic line for every
31
+ style: *"Write the commit message in the following tone style: {style}.
32
+ Apply this tone to both the headline and the bullet points."*
33
+ - `options.py::styles()` holds per-style `description` and `example` used by
34
+ `git cai -l style`, hand-synced with the allowlist.
35
+
36
+ ## Decisions
37
+
38
+ 1. **Custom style entry shape (Option 3 — hybrid):** `instruction` is required;
39
+ `description` and `example` are optional.
40
+ 2. **Built-in names are protected (Option 2):** a `custom_styles` key that
41
+ collides with a built-in name or with `none` is a config validation error.
42
+ 3. **Prompt assembly (Option 3):** the style's `instruction` leads, followed by
43
+ a fixed suffix: `"{instruction} Apply this tone to both the headline and the
44
+ bullet points."`
45
+ 4. **Listing (Option 1 — merged + marker):** `git cai -l style` shows built-in
46
+ and custom styles in one list; custom entries are tagged `name (custom)`.
47
+ 5. **Override short flags (F):** the invocation-override options get short flags
48
+ and are added to the help text — `-y/--style`, `-e/--emoji`, `-L/--language`,
49
+ `-E/--temperature`. `--temperature` previously used `-e`; it moves to `-E` so
50
+ `--emoji` can take the mnemonic `-e`. `-L` and `-E` each differ from an
51
+ existing flag (`-l/--list`, `-e`) only by case, which Typer/Click handle.
52
+
53
+ ## Components
54
+
55
+ ### 1. `core/styles.py` (new — single registry)
56
+
57
+ Dependency-free module (same rationale as `prompts_fallback.py`: importable by
58
+ `config`, `validate`, `llm`, `options` without creating import cycles). Single
59
+ source of truth for built-in styles, each carrying all three fields:
60
+
61
+ ```python
62
+ BUILTIN_STYLES: dict[str, dict[str, str]] = {
63
+ "professional": {
64
+ "instruction": "Write in a clear, concise, and formal tone.",
65
+ "description": "Clear, concise, and formal. Default style.",
66
+ "example": "...",
67
+ },
68
+ # neutral, friendly, funny, excited, sarcastic, apologetic, academic
69
+ }
70
+
71
+ RESERVED_STYLE_NAMES = set(BUILTIN_STYLES) | {"none"}
72
+ ```
73
+
74
+ Replaces the allowlist in `validate.py` and the inline descriptions/examples in
75
+ `options.py::styles()`.
76
+
77
+ ### 2. Config — `custom_styles` key
78
+
79
+ - New top-level key in `DEFAULT_CONFIG`, default `{}`; documented in
80
+ `cai_config.yml`.
81
+ - Repo config remains authoritative (per `CLAUDE.md`), so a repo can ship a
82
+ house style set.
83
+ - Entry shape: `instruction` required; `description`, `example` optional.
84
+
85
+ Example:
86
+
87
+ ```yaml
88
+ custom_styles:
89
+ sardonic:
90
+ instruction: "Use dry, ironic understatement; keep the subject imperative."
91
+ description: "Dry, ironic tone." # optional
92
+ example: "Heroically fixed the bug we wrote yesterday." # optional
93
+ ```
94
+
95
+ ### 3. Validation (`core/validate.py`)
96
+
97
+ - **`_validate_custom_styles(custom_styles)`** (new): for each entry —
98
+ - key is a non-empty string and **not** in `RESERVED_STYLE_NAMES`
99
+ (collision → `ValueError`);
100
+ - value has a non-empty `instruction`;
101
+ - `description` / `example`, if present, are strings.
102
+ Called during config load; fails fast with a message naming the offending key
103
+ and the problem.
104
+ - **`_validate_style(style, custom_names)`**: signature gains the set of
105
+ configured custom style names. Accepts built-ins, `none`, or any custom name;
106
+ otherwise raises with the merged allowed list. Call sites in `config.py`
107
+ (default load and `--style` override) already have `custom_styles` available
108
+ and pass the names through.
109
+
110
+ ### 4. Prompt assembly (`core/llm.py::_style_instruction`)
111
+
112
+ Look up the active style in `BUILTIN_STYLES` merged with config `custom_styles`,
113
+ then:
114
+
115
+ ```python
116
+ return f"{entry['instruction']} Apply this tone to both the headline and the bullet points."
117
+ ```
118
+
119
+ `none` / `None` → `""` (unchanged). The old generic hardcoded line is removed;
120
+ every style now contributes its own flavor plus the fixed suffix.
121
+
122
+ ### 5. Listing (`core/options.py::styles`)
123
+
124
+ Build the returned dict from `BUILTIN_STYLES` + config `custom_styles`. Custom
125
+ entries are tagged `name (custom)`. When `description` is absent, fall back to
126
+ the `instruction` text; when `example` is absent, omit the example line.
127
+
128
+ ### 6. CLI short flags + help visibility (`cli/cli.py`, `cli/helptext.py`)
129
+
130
+ - Update the override `typer.Option` definitions in `cli.py`:
131
+ - `--style` → add `"-y"`
132
+ - `--emoji` → add `"-e"`
133
+ - `--language` → add `"-L"`
134
+ - `--temperature` → change its existing `"-e"` to `"-E"` (freeing `-e` for
135
+ emoji)
136
+ - Add the four flags to the hand-maintained `HELP_TEXT` in `helptext.py`, in
137
+ the alphabetical position used by the existing list, e.g.:
138
+
139
+ ```text
140
+ -E, --temperature FLOAT Override the active provider's sampling temperature for this invocation
141
+ -e, --emoji Toggle emoji prefixes (use --no-emoji to disable)
142
+ -L, --language CODE Override the commit message language (e.g. de, fr, none)
143
+ -y, --style NAME Override the commit message style (e.g. funny, neutral, none)
144
+ ```
145
+
146
+ The `-l, --list` line already documents `style` as a list type and stays as
147
+ is.
148
+
149
+ ## Data flow
150
+
151
+ ```text
152
+ cai_config.yml (custom_styles)
153
+
154
+
155
+ config load ──► _validate_custom_styles() (structure + collisions)
156
+
157
+ ├──► _validate_style(style, custom_names) (default + --style override)
158
+
159
+
160
+ config["style"], config["custom_styles"]
161
+
162
+ ├──► llm._style_instruction() → "{instruction} <suffix>" (BUILTIN_STYLES ∪ custom)
163
+
164
+ └──► options.styles() → merged list with (custom) markers
165
+ ```
166
+
167
+ ## Error handling
168
+
169
+ An invalid `custom_styles` block fails fast at config load with a clear message
170
+ (which key, what is wrong), consistent with how `_validate_style` already
171
+ reports invalid styles. An unknown `--style` value raises with the merged
172
+ allowed list (built-ins + `none` + configured custom names).
173
+
174
+ ## Testing
175
+
176
+ All mocked — no real API/HTTP calls (CI has no creds; tokens cost money):
177
+
178
+ - `_validate_custom_styles`: happy path; missing/empty `instruction`;
179
+ collision with a built-in name; collision with `none`; non-string optional
180
+ fields.
181
+ - `_validate_style`: accepts a configured custom name; rejects an unknown name
182
+ with the merged allowed list in the message.
183
+ - `_style_instruction`: builds `"{instruction} <suffix>"` for a built-in, for a
184
+ custom style, and returns `""` for `none` / `None`.
185
+ - `options.styles()`: merges built-ins + custom; applies `(custom)` marker;
186
+ falls back `description → instruction`; omits `example` when absent.
187
+ - CLI short flags: invoking with `-y`, `-e`, `-L`, `-E` is parsed equivalently
188
+ to the long form (via Typer's `CliRunner`, no network), and `git cai -h`
189
+ output contains all four override flags.
190
+
191
+ ## Documentation
192
+
193
+ Update **`docs/git-cai.txt` only** (per standing preference — do not touch
194
+ `docs/man/git-cai.1`, `README.md`, or `CLAUDE.md` unless asked): document the
195
+ `custom_styles` key and entry shape, and the new `-y/-e/-L/-E` override short
196
+ flags.
197
+
198
+ ## Out of scope (YAGNI)
199
+
200
+ - Overriding/retuning built-in styles via config (built-ins are protected).
201
+ - Free-form passthrough of arbitrary style strings (rejected in favor of an
202
+ explicit custom registry).
203
+ - `random`/surprise style selection.
@@ -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.14.1'
22
- __version_tuple__ = version_tuple = (0, 14, 1)
21
+ __version__ = version = '0.14.2'
22
+ __version_tuple__ = version_tuple = (0, 14, 2)
23
23
 
24
- __commit_id__ = commit_id = 'gbd1b6f7b7'
24
+ __commit_id__ = commit_id = 'gf575cff14'
@@ -181,22 +181,25 @@ def callback( # pylint: disable=too-many-arguments,too-many-positional-argument
181
181
  ),
182
182
  temperature: float = typer.Option(
183
183
  None,
184
- "-e",
184
+ "-E",
185
185
  "--temperature",
186
186
  help="Override the active provider's sampling temperature for this invocation.",
187
187
  ),
188
188
  style: str = typer.Option(
189
189
  None,
190
+ "-y",
190
191
  "--style",
191
192
  help="Override the commit message style for this invocation (e.g. funny, neutral, none).",
192
193
  ),
193
194
  language: str = typer.Option(
194
195
  None,
196
+ "-L",
195
197
  "--language",
196
198
  help="Override the commit message language code for this invocation (e.g. de, fr, none).",
197
199
  ),
198
200
  emoji: bool | None = typer.Option(
199
201
  None,
202
+ "-e",
200
203
  "--emoji/--no-emoji",
201
204
  help="Override emoji usage for this invocation. Use --no-emoji to disable when config enables it.",
202
205
  ),
@@ -21,6 +21,8 @@ Flags:
21
21
  -C, --conventional Use Conventional Commits format (type(scope): description)
22
22
  -c, --crazy Commit immediately without opening editor (trust LLM output)
23
23
  -d, --debug Enable debug logging
24
+ -E, --temperature FLOAT Override the active provider's sampling temperature for this invocation
25
+ -e, --emoji Toggle emoji prefixes (use --no-emoji to disable)
24
26
  -F, --full-files Send the full contents of affected files alongside the diff
25
27
  -f, --files PATH Limit the diff (and full files) to PATH; repeat the flag for multiple files
26
28
  -g, --generate-config Generate default cai_config.yml in the current directory
@@ -28,6 +30,7 @@ Flags:
28
30
  -h, --help Show this help message or opens manual
29
31
  -I, --init Interactive setup wizard (writes home config and tokens.yml)
30
32
  -i, --install-completion Install shell completion for git-cai
33
+ -L, --language CODE Override the commit message language (e.g. de, fr, none)
31
34
  -l, --list [TYPE] List information. TYPE: config, editor, language,
32
35
  model, path, provider, style
33
36
  -m, --model NAME Override model for this invocation (requires --provider)
@@ -48,6 +51,7 @@ Flags:
48
51
  -u, --update Check for updates
49
52
  -v, --version Show installed version
50
53
  -x, --context TEXT Provide extra context for the LLM (e.g. ticket number, reason)
54
+ -y, --style NAME Override the commit message style (e.g. funny, neutral, none)
51
55
  -z, --stats Show local-only usage analytics (commits, tokens, latency)
52
56
  --since YYYY-MM-DD Filter --stats to events on or after this date
53
57
  --json Render --stats output as JSON instead of text
@@ -7,7 +7,7 @@ Both modules can safely import from here without creating import cycles.
7
7
 
8
8
  HARDCODED_COMMIT_PROMPT = (
9
9
  "You are an expert software engineer assistant.\n"
10
- "Your task is to generate a concise, professional git commit message\n"
10
+ "Your task is to generate a concise git commit message\n"
11
11
  "from the provided git diff.\n"
12
12
  "\n"
13
13
  "Follow the seven rules of a great git commit message:\n"
@@ -44,7 +44,7 @@ HARDCODED_COMMIT_PROMPT = (
44
44
 
45
45
  HARDCODED_FULL_FILES_PROMPT = (
46
46
  "You are an expert software engineer assistant.\n"
47
- "Your task is to generate a concise, professional git commit message\n"
47
+ "Your task is to generate a concise git commit message\n"
48
48
  "from the provided git diff AND the full contents of the affected files.\n"
49
49
  "\n"
50
50
  "The input is structured as follows:\n"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: git-cai-cli
3
- Version: 0.14.1
3
+ Version: 0.14.2
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
@@ -274,7 +274,7 @@ In addition to `git cai`, the following options are available:
274
274
  - `-C`, `--conventional` – use Conventional Commits format (`type(scope): description`)
275
275
  - `-c`, `--crazy` – Trust the LLM and commit without checking
276
276
  - `-d`, `--debug` – enable debug logging
277
- - `-e`, `--temperature` `TEMPERATURE` – override the active provider's sampling temperature for this invocation (provider-scoped, like `-m`)
277
+ - `-E`, `--temperature` `TEMPERATURE` – override the active provider's sampling temperature for this invocation (provider scoped, like `-m`)
278
278
  - `-F`, `--full-files` – attach the full contents of affected files alongside the diff (uses `full_files_prompt.md`)
279
279
  - `-f`, `--files` `PATH` – limit the diff (and full-file content, if enabled) to PATH; repeat for multiple files
280
280
  - `-g`, `--generate-config` – generate the default `cai_config.yml` in the current directory
@@ -297,9 +297,9 @@ In addition to `git cai`, the following options are available:
297
297
  - `--base` `BRANCH` – explicit base branch for `--PR` (overrides auto-detection: `origin/HEAD` → `main` → `master`)
298
298
  - `-S`, `--set` – set a config value (`key=value`) in repo config (requires existing repo config)
299
299
  - `-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
300
- - `--style` `STYLE` – override the commit message style for this invocation (e.g. `funny`, `neutral`, `none`); validated against the supported styles
301
- - `--language` `CODE` – override the commit message language for this invocation (e.g. `de`, `fr`, `none`); validated against supported codes
302
- - `--emoji` / `--no-emoji` – override emoji usage for this invocation (use `--no-emoji` to disable when config enables it)
300
+ - `-y`, `--style` `STYLE` – override the commit message style for this invocation (e.g. `funny`, `neutral`, `none`); validated against the supported styles
301
+ - `-L`, `--language` `CODE` – override the commit message language for this invocation (e.g. `de`, `fr`, `none`); validated against supported codes
302
+ - `-e`, `--emoji` / `--no-emoji` – override emoji usage for this invocation (use `--no-emoji` to disable when config enables it)
303
303
  - `-T`, `--timeout` `SECONDS` – HTTP timeout for this invocation (overrides config)
304
304
  - `-t`, `--time` – measure and log commit message generation time
305
305
  - `-u`, `--update` – check for updates
@@ -34,6 +34,7 @@ uv.lock
34
34
  .linters/pyrightconfig.json
35
35
  docs/git-cai.txt
36
36
  docs/man/git-cai.1
37
+ docs/superpowers/specs/2026-06-14-custom-styles-design.md
37
38
  src/git_cai_cli/__init__.py
38
39
  src/git_cai_cli/_version.py
39
40
  src/git_cai_cli/main.py
@@ -2,6 +2,7 @@
2
2
  Unit tests for prompt loading, custom prompt files, and 'none' config values.
3
3
  """
4
4
 
5
+ from pathlib import Path
5
6
  from unittest.mock import patch
6
7
 
7
8
  import pytest
@@ -218,6 +219,30 @@ class TestLoadPromptFileHardcoded:
218
219
 
219
220
  assert result == HARDCODED_SQUASH_PROMPT
220
221
 
222
+ def test_hardcoded_commit_prompt_is_tone_neutral(self):
223
+ """
224
+ The hardcoded base prompts must not bake in a tone word, so the style
225
+ instruction is the single source of tone (and an override wins cleanly).
226
+ """
227
+ assert "professional" not in HARDCODED_COMMIT_PROMPT
228
+ assert "professional" not in HARDCODED_FULL_FILES_PROMPT
229
+
230
+ def test_style_override_is_authoritative_in_fallback(self, base_config):
231
+ """
232
+ With the hardcoded fallback (no config files) and style overridden to
233
+ 'funny', the built prompt carries only the chosen style — not the old
234
+ hardcoded 'professional' tone.
235
+ """
236
+ base_config["style"] = "funny"
237
+ gen = CommitMessageGenerator("tok", base_config, "openai")
238
+
239
+ empty_dir = Path("/nonexistent-cai-config-dir")
240
+ with patch("git_cai_cli.core.llm.CONFIG_DIR", empty_dir):
241
+ prompt = gen._build_commit_prompt()
242
+
243
+ assert "funny" in prompt
244
+ assert "professional" not in prompt
245
+
221
246
 
222
247
  # ---------------------------------------------------------------------------
223
248
  # load_prompt_file: logging
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes