Skip to content

Commit 8c59d6c

Browse files
committed
Merge branch 'release/v1.9.5'
2 parents ab6a6cc + 8f46fe4 commit 8c59d6c

7 files changed

Lines changed: 354 additions & 66 deletions

File tree

‎CLAUDE.md‎

Lines changed: 261 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,261 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Build, Test, and Development Commands
6+
7+
### Building
8+
```bash
9+
# Build in debug mode
10+
cargo build
11+
12+
# Build optimized release binary
13+
cargo build --release
14+
15+
# Check code without building (faster feedback)
16+
cargo check
17+
```
18+
19+
### Testing
20+
```bash
21+
# Run all tests
22+
cargo test --all
23+
24+
# Run tests with output displayed
25+
cargo test --all -- --nocapture
26+
27+
# Run a specific test
28+
cargo test test_prompt -- --nocapture
29+
30+
# Run tests across all Rust versions (stable, beta, nightly)
31+
cargo test --locked
32+
```
33+
34+
### Code Quality
35+
```bash
36+
# Format code (required before commit)
37+
cargo fmt --all
38+
39+
# Check formatting without making changes
40+
cargo fmt --all -- --check
41+
42+
# Run clippy linter
43+
cargo clippy --all-targets --all-features -- -D warnings
44+
45+
# Security audit dependencies
46+
cargo audit
47+
```
48+
49+
### Local Development
50+
```bash
51+
# Run the binary locally against current repo
52+
./target/debug/aigitcommit
53+
54+
# Run with verbose output for debugging
55+
./target/debug/aigitcommit -v
56+
57+
# Check environment configuration
58+
cargo run -- --check-env
59+
60+
# Test API connectivity
61+
cargo run -- --check-model
62+
```
63+
64+
### Installation for Development
65+
```bash
66+
# Install locally for testing in other repos
67+
cargo install --path .
68+
```
69+
70+
## Code Architecture
71+
72+
### High-Level Overview
73+
74+
AIGitCommit is a CLI tool that generates semantic commit messages from staged Git changes using AI. The architecture follows a clear separation of concerns:
75+
76+
1. **CLI Layer** (`cli.rs`): Parses command-line arguments and options using clap
77+
2. **Git Layer** (`git/`): Provides Git operations via git2 (diff, logs, commit)
78+
3. **AI Layer** (`openai.rs`): Interfaces with OpenAI-compatible APIs
79+
<<<<<<< HEAD
80+
4. **Cache Layer** (`cache.rs`): Provides lightweight, stable FNV-1a-hashed on-disk caching of responses
81+
5. **Utilities** (`utils.rs`): Shared utilities for output formatting, environment config, file operations
82+
83+
### Module Responsibilities
84+
85+
#### `main.rs` - Application Entry Point
86+
- Orchestrates the workflow: parse args → get git info → call AI → format output → optionally commit
87+
- Handles three main flows:
88+
- Hook installation (`install-hook` subcommand)
89+
- Environment/model checking (`--check-env`, `--check-model`)
90+
- Main workflow: analyze staged changes → generate message → display/commit
91+
- Initializes tracing/logging based on verbosity flag (`-v`/`--verbose`)
92+
93+
#### `cli.rs` - Command-Line Interface
94+
- Defines `Cli` struct with all command-line flags and options
95+
- Supports subcommands: `install-hook` for git hook setup
96+
- Options include: `--commit`, `--json`, `--copy-to-clipboard`, `--signoff`, `--yes`, `--no-cache`, `--clear-cache`, `--no-table`, `--save`, etc.
97+
- Uses clap's derive macros for parsing and help text generation
98+
99+
#### `git/repository.rs` - Git Operations
100+
- `Repository` struct wraps git2::Repository for high-level operations
101+
- `get_diff()`: Gets staged changes as lines (`git diff --cached`)
102+
- `get_logs(count)`: Gets recent commit messages for context on style/conventions
103+
- `commit(&message)`: Creates a commit with the generated message
104+
- `get_author()`: Retrieves author info from git config
105+
- Filters out common noise (lock files, generated code) from diffs
106+
107+
#### `git/message.rs` - Commit Message Representation
108+
- `GitMessage` struct represents a formatted commit message
109+
- Follows Conventional Commits specification (type: scope: title + body)
110+
- Supports Signed-off-by appending
111+
- Handles serialization to different formats (JSON, plain text, table)
112+
113+
#### `openai.rs` - AI Integration
114+
- `OpenAI` struct wraps async-openai's Client
115+
- Configures API endpoint, authentication, proxy, and timeouts from environment
116+
- `chat()`: Sends messages to OpenAI API and returns response
117+
- `check_model()`: Verifies model availability before generating messages
118+
- `prompt()`: Uses Askama templates to format user prompt with logs and diffs
119+
- Template path: `templates/user.txt`
120+
121+
#### `cache.rs` - Response Cache Layer
122+
- `Cache` struct manages file-based caching under `<repo>/.git/aigitcommit-cache/`
123+
- Keys are derived from stable 64-bit FNV-1a hashing of variables that alter prompt inputs (model name, system prompt, staged diff lines, and recent git commit log messages).
124+
- When staged diffs and surrounding context are unchanged, cached responses are reused immediately, avoiding redundant API calls
125+
- Exposes options to bypass (`--no-cache`) or clear (`--clear-cache`)
126+
127+
#### `utils.rs` - Shared Utilities
128+
- Environment variable reading with fallbacks
129+
- Output formatting: `OutputFormat` enum supporting Table, JSON, and plain text
130+
- Hook installation logic
131+
- File I/O operations (save to file)
132+
- Signoff configuration handling (env vars vs git config)
133+
134+
### Build System
135+
136+
- Uses Rust edition 2024
137+
- `build.rs`: Generates `built.rs` (via `built` crate) containing build metadata (version, authors, etc.)
138+
- Optimized release profile: LTO enabled, single codegen unit, stripped binary
139+
- Key dependencies:
140+
- `git2`: Git operations
141+
- `async-openai`: OpenAI API client
142+
- `tokio`: Async runtime
143+
- `askama`: Template rendering for prompts
144+
- `clap`: CLI parsing
145+
- `tabled`: Table formatting
146+
- `cliclack`: Interactive prompts
147+
148+
### Environment Variables
149+
150+
The application can be configured using the following environment variables:
151+
152+
| Variable | Description | Default |
153+
| --------------------- | ----------------------------------------------------------------------------------------------- | --------------------------------- |
154+
| `OPENAI_API_TOKEN` | API token / key for authenticating with the AI service | `""` |
155+
| `OPENAI_API_BASE` | Base URL for the OpenAI-compatible API endpoint | standard OpenAI endpoint |
156+
| `OPENAI_MODEL_NAME` | The model identifier used for generating commit messages | model configured on OpenAI client |
157+
| `OPENAI_API_PROXY` | Optional proxy server address | None |
158+
| `OPENAI_API_TIMEOUT` | Optional HTTP client request timeout in seconds | default no timeout |
159+
| `AIGITCOMMIT_SIGNOFF` | Opt-in truthy value (`1`, `true`, `yes`, `on`) to enforce repository-wide Signed-off-by trailer | None |
160+
| `TEST_REPO_PATH` | Path pointing to a test repository for integration testing | `.` |
161+
162+
### Key Design Decisions
163+
164+
1. **libgit2 over git CLI**: Uses git2 crate instead of spawning `git` command for security and performance
165+
2. **Async Runtime**: Uses tokio for async API calls; main.rs runs with `#[tokio::main]`
166+
3. **Template System**: Uses Askama templates (`templates/user.txt`, `templates/system.txt`) for prompts
167+
4. **Error Handling**: Returns `Result<T, Box<dyn Error>>` for flexible error propagation
168+
5. **Conventional Commits**: Expects AI response in format `title\n\nbody` (split on double newline)
169+
170+
### Testing
171+
172+
- Unit tests are placed alongside code with `#[cfg(test)]` modules
173+
- `openai.rs` contains integration test for prompt generation (requires `TEST_REPO_PATH` env var)
174+
- CI runs tests on stable, beta, and nightly Rust toolchains
175+
- Use `cargo test --all --locked` to match CI behavior
176+
177+
### Style and Development Guidelines
178+
179+
- **Code Formatting**: Ensure all code is cleanly formatted before committing. Check via `cargo fmt --all -- --check` and fix with `cargo fmt --all`.
180+
- **Linting**: Keep code clean and warning-free. Always run `cargo clippy --all-targets --all-features -- -D warnings` to verify.
181+
- **Imports**: Prefer grouping standard library imports first, then internal crates, then third-party dependency modules.
182+
- **Tracing / Logging**: Make clean use of `tracing::debug`, `tracing::trace`, `tracing::warn`, etc. For details regarding CLI tracing levels, refer to logger configuration in [src/main.rs](src/main.rs).
183+
184+
||||||| ab6a6cc
185+
=======
186+
4. **Utilities** (`utils.rs`): Shared utilities for output formatting, environment config, file operations
187+
188+
### Module Responsibilities
189+
190+
#### `main.rs` - Application Entry Point
191+
- Orchestrates the workflow: parse args → get git info → call AI → format output → optionally commit
192+
- Handles three main flows:
193+
- Hook installation (`install-hook` subcommand)
194+
- Environment/model checking (`--check-env`, `--check-model`)
195+
- Main workflow: analyze staged changes → generate message → display/commit
196+
- Initializes tracing/logging based on verbosity flag
197+
198+
#### `cli.rs` - Command-Line Interface
199+
- Defines `Cli` struct with all command-line flags and options
200+
- Supports subcommands: `install-hook` for git hook setup
201+
- Options include: `--commit`, `--json`, `--copy-to-clipboard`, `--signoff`, `--yes`, etc.
202+
- Uses clap's derive macros for parsing and help text generation
203+
204+
#### `git/repository.rs` - Git Operations
205+
- `Repository` struct wraps git2::Repository for high-level operations
206+
- `get_diff()`: Gets staged changes as lines (`git diff --cached`)
207+
- `get_logs(count)`: Gets recent commit messages for context on style/conventions
208+
- `commit(&message)`: Creates a commit with the generated message
209+
- `get_author()`: Retrieves author info from git config
210+
- Filters out common noise (lock files, generated code) from diffs
211+
212+
#### `git/message.rs` - Commit Message Representation
213+
- `GitMessage` struct represents a formatted commit message
214+
- Follows Conventional Commits specification (type: scope: title + body)
215+
- Supports Signed-off-by appending
216+
- Handles serialization to different formats (JSON, plain text, table)
217+
218+
#### `openai.rs` - AI Integration
219+
- `OpenAI` struct wraps async-openai's Client
220+
- Configures API endpoint, authentication, proxy, and timeouts from environment
221+
- `chat()`: Sends messages to OpenAI API and returns response
222+
- `check_model()`: Verifies model availability before generating messages
223+
- `prompt()`: Uses Askama templates to format user prompt with logs and diffs
224+
- Template path: `templates/user.txt`
225+
226+
#### `utils.rs` - Shared Utilities
227+
- Environment variable reading with fallbacks
228+
- Output formatting: `OutputFormat` enum supporting Table, JSON, and plain text
229+
- Hook installation logic
230+
- File I/O operations (save to file)
231+
- Signoff configuration handling (env vars vs git config)
232+
233+
### Build System
234+
235+
- Uses Rust edition 2024
236+
- `build.rs`: Generates `built.rs` (via `built` crate) containing build metadata (version, authors, etc.)
237+
- Optimized release profile: LTO enabled, single codegen unit, stripped binary
238+
- Key dependencies:
239+
- `git2`: Git operations
240+
- `async-openai`: OpenAI API client
241+
- `tokio`: Async runtime
242+
- `askama`: Template rendering for prompts
243+
- `clap`: CLI parsing
244+
- `tabled`: Table formatting
245+
- `cliclack`: Interactive prompts
246+
247+
### Key Design Decisions
248+
249+
1. **libgit2 over git CLI**: Uses git2 crate instead of spawning `git` command for security and performance
250+
2. **Async Runtime**: Uses tokio for async API calls; main.rs runs with `#[tokio::main]`
251+
3. **Template System**: Uses Askama templates (`templates/user.txt`, `templates/system.txt`) for prompts
252+
4. **Error Handling**: Returns `Result<T, Box<dyn Error>>` for flexible error propagation
253+
5. **Conventional Commits**: Expects AI response in format `title\n\nbody` (split on double newline)
254+
255+
### Testing
256+
257+
- Unit tests are placed alongside code with `#[cfg(test)]` modules
258+
- `openai.rs` contains integration test for prompt generation (requires `TEST_REPO_PATH` env var)
259+
- CI runs tests on stable, beta, and nightly Rust toolchains
260+
- Use `cargo test --all --locked` to match CI behavior
261+
>>>>>>> develop

‎Cargo.lock‎

Lines changed: 13 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ dependencies = [
1919

2020
[[package]]
2121
name = "aigitcommit"
22-
version = "1.9.3"
22+
version = "1.9.5"
2323
dependencies = [
2424
"arboard",
2525
"askama",
@@ -170,9 +170,9 @@ dependencies = [
170170

171171
[[package]]
172172
name = "async-openai"
173-
version = "0.37.0"
173+
version = "0.41.0"
174174
source = "registry+https://github.com/rust-lang/crates.io-index"
175-
checksum = "9f704d052a4d91b1da811d7868f274eec9a9f78658793fc25faae077a1455332"
175+
checksum = "3ec57a13b36ba76764870363a9182d8bc9fb49538dc5a948dd2e5224fe65ce40"
176176
dependencies = [
177177
"async-openai-macros",
178178
"base64",
@@ -198,9 +198,9 @@ dependencies = [
198198

199199
[[package]]
200200
name = "async-openai-macros"
201-
version = "0.2.0"
201+
version = "0.3.0"
202202
source = "registry+https://github.com/rust-lang/crates.io-index"
203-
checksum = "3441703ed54a806faf69c99f4fb29247827bd5440ae5a418659a4ac620bb0a08"
203+
checksum = "492a944774207eed3acf425214eadbd6ce84a2b89331164ff1c11bae92b26302"
204204
dependencies = [
205205
"proc-macro2",
206206
"quote",
@@ -823,17 +823,14 @@ dependencies = [
823823

824824
[[package]]
825825
name = "git2"
826-
version = "0.20.4"
826+
version = "0.21.0"
827827
source = "registry+https://github.com/rust-lang/crates.io-index"
828-
checksum = "7b88256088d75a56f8ecfa070513a775dd9107f6530ef14919dac831af9cfe2b"
828+
checksum = "ddddbf932745a6be37109b6112d3ee09696106f848449069d3a57bba937ab82e"
829829
dependencies = [
830830
"bitflags",
831831
"libc",
832832
"libgit2-sys",
833833
"log",
834-
"openssl-probe 0.1.6",
835-
"openssl-sys",
836-
"url",
837834
]
838835

839836
[[package]]
@@ -1274,27 +1271,11 @@ name = "libgit2-sys"
12741271
version = "0.18.5+1.9.4"
12751272
source = "registry+https://github.com/rust-lang/crates.io-index"
12761273
checksum = "005d6ae6eac1912906073e069f7db60b1fa98e052a68227824afe3e3a1c59ca2"
1277-
dependencies = [
1278-
"cc",
1279-
"libc",
1280-
"libssh2-sys",
1281-
"libz-sys",
1282-
"openssl-sys",
1283-
"pkg-config",
1284-
]
1285-
1286-
[[package]]
1287-
name = "libssh2-sys"
1288-
version = "0.3.1"
1289-
source = "registry+https://github.com/rust-lang/crates.io-index"
1290-
checksum = "220e4f05ad4a218192533b300327f5150e809b54c4ec83b5a1d91833601811b9"
12911274
dependencies = [
12921275
"cc",
12931276
"libc",
12941277
"libz-sys",
1295-
"openssl-sys",
12961278
"pkg-config",
1297-
"vcpkg",
12981279
]
12991280

13001281
[[package]]
@@ -1410,7 +1391,7 @@ dependencies = [
14101391
"libc",
14111392
"log",
14121393
"openssl",
1413-
"openssl-probe 0.2.1",
1394+
"openssl-probe",
14141395
"openssl-sys",
14151396
"schannel",
14161397
"security-framework",
@@ -1556,12 +1537,6 @@ dependencies = [
15561537
"syn",
15571538
]
15581539

1559-
[[package]]
1560-
name = "openssl-probe"
1561-
version = "0.1.6"
1562-
source = "registry+https://github.com/rust-lang/crates.io-index"
1563-
checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e"
1564-
15651540
[[package]]
15661541
name = "openssl-probe"
15671542
version = "0.2.1"
@@ -1592,9 +1567,9 @@ dependencies = [
15921567

15931568
[[package]]
15941569
name = "papergrid"
1595-
version = "0.17.0"
1570+
version = "0.18.0"
15961571
source = "registry+https://github.com/rust-lang/crates.io-index"
1597-
checksum = "6978128c8b51d8f4080631ceb2302ab51e32cc6e8615f735ee2f83fd269ae3f1"
1572+
checksum = "d0984e668274d34691bc2b262ef0d115de5fa9973bcdee7ae32213f93099153e"
15981573
dependencies = [
15991574
"bytecount",
16001575
"fnv",
@@ -1987,7 +1962,7 @@ version = "0.8.4"
19871962
source = "registry+https://github.com/rust-lang/crates.io-index"
19881963
checksum = "dab5152771c58876a2146916e53e35057e1a4dfa2b9df0f0305b07f611fdea4d"
19891964
dependencies = [
1990-
"openssl-probe 0.2.1",
1965+
"openssl-probe",
19911966
"rustls-pki-types",
19921967
"schannel",
19931968
"security-framework",
@@ -2319,9 +2294,9 @@ dependencies = [
23192294

23202295
[[package]]
23212296
name = "tabled"
2322-
version = "0.20.0"
2297+
version = "0.21.0"
23232298
source = "registry+https://github.com/rust-lang/crates.io-index"
2324-
checksum = "e39a2ee1fbcd360805a771e1b300f78cc88fec7b8d3e2f71cd37bbf23e725c7d"
2299+
checksum = "b5dc662e6da844ad6e428ad16b57967c9d33c82e16bb1c258326c0c078605dff"
23252300
dependencies = [
23262301
"papergrid",
23272302
"tabled_derive",

0 commit comments

Comments
 (0)