tharlesamaro / laravel-git-ai
AI-powered Git workflow automation for Laravel: smart commit messages, changelog generation, and commit validation using Conventional Commits.
Installs: 2
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/tharlesamaro/laravel-git-ai
Requires
- php: ^8.4
- laravel/ai: ^0.1
- laravel/framework: ^12.0
Requires (Dev)
- orchestra/testbench: ^10.0
- pestphp/pest: ^3.0
- pestphp/pest-plugin-laravel: ^3.0
README
English | Portugues
AI-powered Git workflow automation for Laravel. Generate smart commit messages, changelogs, and validate commits using Conventional Commits.
Why use this package instead of Claude Code directly?
If you already use Claude Code CLI, you might wonder: "Why not just ask Claude to commit for me?" Here's what this package brings to the table:
| Feature | Claude Code alone | Laravel Git AI |
|---|---|---|
| Conventional Commits format | You have to ask every time | Enforced automatically via structured output |
| Consistent JSON schema | Free-form text, may vary | Validated against a strict schema every time |
| Project-specific scopes | Must remember to mention them | Configured once, enforced on every commit |
| Allowed commit types | Manual discipline | Restricted by config, AI cannot use others |
| Multi-language support | Must specify in every prompt | Configured once, always applied |
| Changelog generation | Manual work | Automated from commit history between tags |
| Git hook validation | Not available | Optional hook rejects non-conventional commits |
| Team consistency | Each developer prompts differently | Same rules for everyone via shared config |
| Max diff size control | No control, may exceed context | Auto-truncated to configured limit |
| Works without CLI installed | N/A | Falls back to API providers (Anthropic/OpenAI) |
In short: this package turns AI-generated commits into a repeatable, team-wide standard instead of a one-off prompt.
Features
git:commit-- Generate commit messages from staged changes using AI, following Conventional Commitsgit:changelog-- Generate structured changelogs from commit history between tagsgit:setup-- Interactive configuration wizard- 3 providers -- Anthropic API, OpenAI API, or Claude Code CLI (no API key needed)
- 9 languages -- English, Portuguese, Spanish, French, German, Italian, Japanese, Korean, Chinese
- Scope enforcement -- Restrict commits to project-specific scopes
- Type restriction -- Limit which Conventional Commits types are allowed
- Commit templates -- Built-in (
minimal,detailed) and custom presets for body + footer settings - Body control -- Configure whether AI always includes body, never includes it, or decides automatically
- Footer control -- Toggle BREAKING CHANGE footer, add custom footer lines, control Co-Authored-By trailer
- Git hook -- Optional
commit-msghook to reject non-conventional commits - Structured output -- AI responses are validated against a JSON schema, never free-form text
- Diff truncation -- Large diffs are automatically truncated to fit AI context windows
Requirements
- PHP 8.4+
- Laravel 12+
- One of the following:
- Laravel AI SDK (
laravel/ai) + an API key from Anthropic or OpenAI - OR Claude Code CLI installed on your machine (uses your existing Claude subscription, e.g. Max plan -- no API key required)
- Laravel AI SDK (
Installation
composer require tharlesamaro/laravel-git-ai
Publish the configuration file:
php artisan vendor:publish --tag=git-ai-config
Run the interactive setup:
php artisan git:setup
Provider Setup
Option 1: Anthropic API (default)
GIT_AI_PROVIDER=anthropic ANTHROPIC_API_KEY=your-api-key
Option 2: OpenAI API
GIT_AI_PROVIDER=openai OPENAI_API_KEY=your-api-key
Option 3: Claude Code CLI (no API key)
If you have a Claude subscription (e.g. Max plan) and the Claude Code CLI installed, you can use it directly without any API key:
GIT_AI_PROVIDER=claude-code
Make sure the claude binary is available in your PATH. Install it from: https://docs.anthropic.com/en/docs/claude-code
This option invokes the Claude Code CLI as a subprocess, passing a structured prompt and parsing the JSON response. It consumes from your existing subscription usage -- no separate API tokens needed.
Usage
git:commit -- Generate a commit message
Stage your changes and run:
php artisan git:commit
Or stage everything automatically with the --all (-a) flag:
php artisan git:commit --all
Available options:
| Option | Short | Description |
|---|---|---|
--all |
-a |
Stage all changes before committing |
--template= |
Use a named commit template (e.g. minimal, detailed) |
|
--no-body |
Strip body from the commit message | |
--footer= |
Add custom footer line(s) (can be used multiple times) |
What happens:
- Reads your staged diff (truncated if it exceeds
max_diff_size) - Sends it to the configured AI provider
- Receives a structured response with
type,scope,description,body, andis_breaking_change - Validates the type and scope against your config
- Formats the message following Conventional Commits
- Lets you choose what to do next
Interactive menu:
Staged changes:
src/Models/User.php | 12 ++++++---
src/Http/AuthController.php | 8 ++++--
2 files changed, 14 insertions(+), 6 deletions(-)
Generated commit message:
feat(auth): add email verification on registration
What would you like to do?
> Accept and commit
Edit the message
Generate a new suggestion
Cancel
- Accept and commit -- Creates the commit with the generated message
- Edit the message -- Opens an editor to modify the title and body separately
- Generate a new suggestion -- Calls the AI again for a different message
- Cancel -- Aborts without committing
Example with body and breaking change:
Generated commit message:
feat(api)!: replace REST endpoints with GraphQL
Migrate all API endpoints from REST to GraphQL.
This removes all /api/v1/* routes.
BREAKING CHANGE: replace REST endpoints with GraphQL
git:changelog -- Generate a changelog
php artisan git:changelog
Available options:
| Option | Description | Default |
|---|---|---|
--from= |
Starting tag or commit hash | Latest tag (or first commit if no tags) |
--to= |
Ending tag or commit hash | HEAD |
--tag= |
Version tag for the changelog header | Interactive prompt |
--dry-run |
Preview without writing to file | false |
Examples:
# Auto-detect range (latest tag to HEAD) php artisan git:changelog # From a specific tag to HEAD php artisan git:changelog --from=v1.0.0 # Between two references php artisan git:changelog --from=v1.0.0 --to=v1.1.0 # Specify the version tag upfront php artisan git:changelog --tag=v2.0.0 # Preview without writing to file php artisan git:changelog --dry-run # Combine options php artisan git:changelog --from=v1.0.0 --to=v2.0.0 --tag=v2.0.0 --dry-run
What happens:
- Resolves the starting reference (priority:
--from> latest tag > first commit) - Gets all commits between
fromandto - Parses each commit message using the Conventional Commits format
- Groups commits by type (
feat,fix,docs, etc.) - Sends the grouped commits to the AI for human-readable descriptions
- Formats the output as Markdown with emojis (configurable)
- Shows a preview and asks for confirmation before writing
Output example (CHANGELOG.md):
## [v1.2.0] - 2026-02-11 ### Features - Add email verification during user registration - Implement password reset via SMS ### Bug Fixes - Resolve null pointer when loading user preferences - Fix timezone handling in scheduled notifications ### Documentation - Update API authentication guide with OAuth2 examples
If changelog.with_emojis is enabled (default), section titles include emojis:
### โจ Features ### ๐ Bug Fixes ### ๐ Documentation ### โป๏ธ Code Refactoring ### โก Performance Improvements ### ๐งช Tests ### ๐ฆ Build System ### ๐ง Continuous Integration ### ๐จ Chores ### โช Reverts
The changelog is prepended to the existing file. If CHANGELOG.md already exists, new content is added at the top (below the header), preserving previous entries.
git:setup -- Interactive configuration
php artisan git:setup
The wizard walks you through every configurable option:
- AI provider -- Anthropic API, OpenAI API, or Claude Code CLI
- Language -- English, Portuguese (Brazil), Spanish, French, German, Italian, Japanese, Korean, or Chinese
- Scopes -- Define allowed scopes for your project (e.g.,
auth,api,ui,database) - Types -- Restrict which commit types are allowed (e.g., only
feat,fix,docs) - Body preference -- How the AI handles commit message body (auto, always, never)
- Git hook -- Install a
commit-msghook that rejects non-conventional commits
After setup, it writes config/git-ai.php and shows the environment variables you need to add to .env.
Configuration
All options in config/git-ai.php:
return [ // AI provider: 'anthropic', 'openai', or 'claude-code' 'provider' => env('GIT_AI_PROVIDER', 'anthropic'), // AI model override (null = provider default) // Examples: 'claude-sonnet-4-5-20250929', 'gpt-4o', etc. 'model' => env('GIT_AI_MODEL'), // Language for commit messages and changelog entries // Supported: 'en', 'pt-BR', 'es', 'fr', 'de', 'it', 'ja', 'ko', 'zh' 'language' => env('GIT_AI_LANGUAGE', 'en'), // Allowed commit scopes (empty array = any scope allowed) // The AI will only use scopes from this list // Example: ['auth', 'api', 'ui', 'database', 'config'] 'scopes' => [], // Allowed commit types (empty array = all Conventional Commits types allowed) // The AI will only use types from this list // Example: ['feat', 'fix', 'docs', 'refactor', 'test'] 'types' => [], // Maximum diff size sent to the AI (in characters) // Diffs larger than this are truncated with a warning 'max_diff_size' => env('GIT_AI_MAX_DIFF_SIZE', 15000), // Commit message settings 'commit' => [ // Body behavior: 'auto' (AI decides), 'always' (AI must include), 'never' (stripped from output) 'body' => env('GIT_AI_COMMIT_BODY', 'auto'), // Footer settings 'footer' => [ // Whether to include the BREAKING CHANGE footer when applicable 'breaking_change' => true, // Whether to include a "Co-Authored-By" trailer in commit messages // Set to false to prevent AI attribution lines in commits 'co_authored_by' => env('GIT_AI_CO_AUTHORED_BY', false), // Custom footer lines to append to every commit message // Example: ['Signed-off-by: Name <email@example.com>'] 'lines' => [], ], ], // Named presets that bundle body + footer settings // Built-in templates: 'minimal', 'detailed' 'templates' => [ // Default template (null = no template, use 'commit' settings directly) 'default' => env('GIT_AI_TEMPLATE'), // Custom template definitions 'presets' => [ // 'my-team' => [ // 'body' => 'always', // 'footer' => [ // 'breaking_change' => true, // 'co_authored_by' => false, // 'lines' => ['Signed-off-by: Team <team@example.com>'], // ], // ], ], ], // Changelog generation settings 'changelog' => [ // File path relative to project root 'path' => 'CHANGELOG.md', // Include emojis in section titles (e.g., "### โจ Features") 'with_emojis' => true, ], // Git hook settings 'hook' => [ // Whether the commit-msg validation hook is enabled 'enabled' => false, // When true, rejects non-conventional commits // When false, only displays a warning 'strict' => true, ], ];
Environment variables
| Variable | Description | Default |
|---|---|---|
GIT_AI_PROVIDER |
AI provider (anthropic, openai, claude-code) |
anthropic |
GIT_AI_MODEL |
AI model override | Provider default |
GIT_AI_LANGUAGE |
Commit message language | en |
GIT_AI_MAX_DIFF_SIZE |
Max diff size in characters | 15000 |
GIT_AI_COMMIT_BODY |
Body behavior (auto, always, never) |
auto |
GIT_AI_CO_AUTHORED_BY |
Include Co-Authored-By trailer | false |
GIT_AI_TEMPLATE |
Default commit template name | -- |
ANTHROPIC_API_KEY |
Anthropic API key (when provider is anthropic) |
-- |
OPENAI_API_KEY |
OpenAI API key (when provider is openai) |
-- |
Commit Templates
Templates bundle body and footer preferences into named presets. They're entirely optional -- without a template, the commit config section is used directly.
Built-in templates
| Template | Body | BREAKING CHANGE footer | Use case |
|---|---|---|---|
minimal |
Never | No | Quick, one-line commits |
detailed |
Always | Yes | Thorough commits with full context |
Using templates
# Use a built-in template php artisan git:commit --template=minimal # Override body on the fly php artisan git:commit --no-body # Add custom footers php artisan git:commit --footer="Signed-off-by: Name <email>" php artisan git:commit --footer="Reviewed-by: Alice" --footer="Tested-by: Bob" # Combine options php artisan git:commit --template=detailed --footer="Signed-off-by: Team <team@example.com>"
Set a default template
In your .env:
GIT_AI_TEMPLATE=minimal
Or in config/git-ai.php:
'templates' => [ 'default' => 'minimal', ],
Custom templates
Define your own presets in config/git-ai.php:
'templates' => [ 'default' => null, 'presets' => [ 'my-team' => [ 'body' => 'always', 'footer' => [ 'breaking_change' => true, 'co_authored_by' => false, 'lines' => ['Signed-off-by: Team <team@example.com>'], ], ], 'quick' => [ 'body' => 'never', 'footer' => [ 'breaking_change' => false, 'lines' => [], ], ], ], ],
Then use it:
php artisan git:commit --template=my-team
Disabling Co-Authored-By
By default, the Co-Authored-By trailer is not included in commit messages. If you want to enable it:
// config/git-ai.php 'commit' => [ 'footer' => [ 'co_authored_by' => true, ], ],
Or via environment variable:
GIT_AI_CO_AUTHORED_BY=true
When enabled, commits will include a trailer like:
Co-Authored-By: Claude <noreply@anthropic.com>
Set it to false (default) to keep commits clean without AI attribution.
Git Hook
The package includes an optional commit-msg git hook that validates commit messages against the Conventional Commits specification.
What it validates:
- The message must match the format:
<type>(<scope>): <description> - The type must be one of:
feat,fix,docs,style,refactor,perf,test,build,ci,chore,revert - The first line must not exceed 72 characters
- Merge commits, reverts, fixups, and squashes are automatically allowed
Install the hook:
# Via the setup wizard php artisan git:setup # Or manually copy the hook file cp vendor/tharlesamaro/laravel-git-ai/stubs/commit-msg .git/hooks/commit-msg chmod +x .git/hooks/commit-msg
Example of a rejected commit:
$ git commit -m "updated stuff"
Invalid commit message format!
Your message:
updated stuff
Expected format:
<type>(<scope>): <description>
Valid types: feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert
Examples:
feat(auth): add OAuth2 login support
fix: resolve null pointer in user service
docs(readme): update installation instructions
Tip: Use 'php artisan git:commit' to generate valid messages automatically.
Conventional Commits
This package follows the Conventional Commits v1.0.0 specification:
<type>(<scope>): <description>
[optional body]
[optional footer(s)]
Supported types
| Type | Emoji | Description |
|---|---|---|
feat |
โจ | A new feature |
fix |
๐ | A bug fix |
docs |
๐ | Documentation only changes |
style |
๐ | Code style changes (formatting, semicolons, etc.) |
refactor |
โป๏ธ | Code refactoring (no feature or fix) |
perf |
โก | Performance improvements |
test |
๐งช | Adding or fixing tests |
build |
๐ฆ | Build system or dependency changes |
ci |
๐ง | CI configuration changes |
chore |
๐จ | Other changes (tooling, configs, etc.) |
revert |
โช | Reverts a previous commit |
Breaking changes
Breaking changes are indicated by:
- An
!after the type/scope:feat(api)!: remove deprecated endpoints - A
BREAKING CHANGE:footer in the body
The AI detects breaking changes automatically from the diff and sets is_breaking_change accordingly.
Architecture
The package uses a service abstraction layer (AiService contract) that allows swapping between providers without changing command logic:
AgentAiService-- Uses the Laravel AI SDK agents with structured output for API-based providers (Anthropic, OpenAI)ClaudeCodeAiService-- Invokes theclaudeCLI as a subprocess for users with a Claude subscription
The provider is resolved at runtime based on config('git-ai.provider'). Both implementations return the same structured array format, ensuring consistent behavior regardless of the provider.
Testing
composer test
This package uses Pest PHP for testing. To run with coverage:
composer test-coverage
Using Docker:
docker compose run --rm test
Contributing
Contributions are welcome! Please follow Conventional Commits for your commit messages.
- Fork the repository
- Create your feature branch (
git checkout -b feat/amazing-feature) - Commit your changes (
php artisan git:commit๐) - Push to the branch (
git push origin feat/amazing-feature) - Open a Pull Request
License
The MIT License (MIT). See LICENSE for details.