Skip to content

Repository files navigation

commit-it

Standardized git commits with AI message generation, interactive prompts, validation, and GitHub integration. Works as a CLI, inside AI agents, and as a programmable TypeScript API.

Why commit-it?

  • AI-powered commits — Claude, Codex, OpenCode, or Cursor Agent generates messages from your diff. No API keys to manage.
  • Works inside AI agents — Claude Code, Cursor, OpenCode, Aider, and Cline auto-detect and use your project's commit schema. Zero config.
  • Interactive when you want it — guided flow for type, scope, message, issues, and co-authors.
  • Your format, your rules — custom templates, custom types, regex-based rules. Use conventional commits, gitmoji, JIRA-prefixed, or invent your own.
  • Validates every commit — enforces max length, required scopes, custom regex rules, and more.
  • GitHub-native — search and link issues, create branches from issues, suggest scopes from PR labels.
  • Fully programmable — every feature is importable as a TypeScript API for scripts, CI, and custom tools.

Install

npm install -g commit-it # global
npx commit-it # or run without installing
bunx commit-it # bun users

Also available as cit and commit:

cit --ai # AI commit
commit -t fix -m "resolve bug"# non-interactive

After installing, run the setup wizard to configure commit-it for your environment:

commit-it setup

This walks you through picking an AI provider (Claude, Codex, OpenCode, Cursor Agent), selecting a model, choosing a commit style (conventional or gitmoji), and setting your preferred editor. The config is saved to ~/.commit-it/config.json and applies globally. You can re-run it anytime to change your settings.

Quick Start

# Stage your changes + let AI write the message
commit-it --ai --all
# Or go interactive
commit-it --all

The --all (-a) flag stages all changes before committing, so you can skip git add entirely.

That's it. No config needed — commit-it uses sensible defaults (conventional commits, validation on, GitHub on).

Optional setup

commit-it init # generate a config file
commit-it setup-alias # type "commit" instead of "commit-it"
commit-it install-hook # validate every git commit automatically

Custom Formats

Don't like conventional commits or gitmoji? Define your own format. commit-it's template engine, custom types, and regex rules let you enforce any commit style your team uses.

Custom template

Override the commit message shape with {{placeholders}} and {{#conditional}} sections:

exportdefaultdefineConfig({// Bracket-style scopes: "fix[auth]: token refresh"template: '{{type}}{{#scope}}[{{scope}}]{{/scope}}: {{message}}',})
exportdefaultdefineConfig({// Ticket-prefix style: "[PROJ-123] add login page"template: '[{{type}}] {{message}}',})

Available placeholders: {{type}}, {{scope}}, {{message}}, {{breaking}}. Wrap any placeholder in {{#field}}...{{/field}} to only render that section when the field has a value.

Custom types

Replace the default feat/fix/docs types with whatever vocabulary fits your team:

exportdefaultdefineConfig({validation: {allowedTypes: ['add','change','remove','fix','security','deploy'],},})

Custom format rules

Enforce any commit format with regex-based rules — Angular-style, JIRA-prefixed, uppercase categories, or anything else:

exportdefaultdefineConfig({validation: {customRules: [{name: 'require-ticket',pattern: '[A-Z]+-\\d+',message: 'Must include ticket (e.g., PROJ-123)',level: 'error',invert: false,// fail when pattern DOESN'T match},],},})

See Configuration for the full reference and Example Configurations for complete setups.

AI Commit Messages

commit-it shells out to whichever AI CLI you have installed. No API keys needed.

ProviderCLIAuto-detected
Claude CodeclaudeYes
OpenAI CodexcodexYes
OpenCodeopencodeYes
Cursor AgentagentYes
Customany CLIConfigurable
# Stage changes, then generate
commit-it --ai
# Use a specific provider
commit-it --ai --provider codex
# Stage everything + AI + no prompts (perfect for scripts)
commit-it -a --ai -y
# Split staged changes into multiple logical commits
commit-it --all --multi

The AI analyzes your staged diff and returns a structured suggestion (type, scope, message, body). You can accept, edit in $EDITOR, or decline and go manual.

First-run setup

On first use, a quick wizard helps you pick a provider, model, and commit style. Or create ~/.commit-it/config.json:

{
"ai": { "auto": true, "provider": "claude" },
"preset": "conventional",
"editor": "code --wait"
}

Set ai.auto to true to always generate AI suggestions without the --ai flag.

Agent Mode

When running inside an AI agent (Claude Code, Cursor, OpenCode, Aider, Cline, or any non-TTY environment), commit-it adapts automatically:

  1. No flags — outputs the project's commit schema as JSON so the agent can generate a conforming message
  2. -m with a message — auto-parses type and scope, validates, and commits directly
  3. AI generation is skipped — the calling agent already has context
# Agent discovers the schema
$ commit-it
# → outputs JSON: { preset, template, types, scopes, validation, usage }# Agent commits using the schema
commit-it -m "feat(cli): add agent detection"

Detection triggers: stdin is not a TTY, or env vars like CLAUDE_CODE, CURSOR_AGENT, CODEX_CLI, OPENCODE, AIDER, CLINE are set.

Interactive Flow

Running commit-it walks through:

1. Select type ─── feat, fix, docs, refactor, etc.
2. Select scope ─── suggested from changed files + PR labels
3. Breaking change? ─── adds ! and BREAKING CHANGE footer
4. Reference issues ─── search GitHub issues, pick Closes/Fixes/Ref
5. Commit message ─── short description (pre-filled from AI)
6. Body ─── optional detailed description
7. Co-authors ─── from config, GitHub, or manual entry
8. Preview & confirm ─── see full message, validate, commit

Smart defaults at every step: AI suggestions > previous commit > PR labels > config.

GitHub Integration

Requires the GitHub CLI (gh). All features degrade gracefully if gh isn't installed.

  • PR context — detects current PR, suggests commit type from labels (bug -> fix, feature -> feat)
  • Scope from labels — labels matching scopeLabelPatterns become scope suggestions
  • Issue linking — search and link multiple issues per commit with different actions
  • Branch creationcommit-it branch creates branches from issue titles
  • Co-author discovery — fetches repo collaborators for co-author selection

Issue references

You can reference multiple GitHub issues in a single commit. During the interactive flow, you'll be prompted to search for issues and choose how each one is referenced. After adding an issue, you're asked if you want to add another — repeat as many times as needed.

Each issue reference uses one of four actions:

ActionEffect
ClosesAuto-closes the issue when the commit is merged
FixesAuto-closes the issue when the commit is merged
ResolvesAuto-closes the issue when the commit is merged
RefMentions the issue without closing it

References are appended as a footer in the commit message:

feat(auth): add OAuth support
Closes #42
Ref #99
Fixes #101

Use Ref when you want to link related context (e.g., a tracking issue or design doc) without auto-closing anything.

In agent mode, the schema output includes validation.requireIssue so agents know whether issue references are expected. Since there's no --issue CLI flag, agents include GitHub keywords directly in the commit message:

commit-it -m "feat(auth): add OAuth support" --body "Closes #42Ref #99"

GitHub recognizes Closes, Fixes, Resolves, and Ref keywords anywhere in the commit message body or footer.

To require an issue reference on every commit, set validation.requireIssue:

exportdefaultdefineConfig({validation: {requireIssue: true,},})

Programmatic usage:

awaitdirectCommit({type: 'feat',message: 'add OAuth support',issueRefs: [{action: 'Closes',number: 42},{action: 'Ref',number: 99},{action: 'Fixes',number: 101},],})

Non-Interactive Mode

Skip all prompts by passing a message:

# Full conventional commit string (type auto-parsed)
commit-it -m "feat(auth): add OAuth support"# Explicit type + message
commit-it -t fix -s auth -m "resolve token refresh"# With body, breaking change, co-author
commit-it -t feat -m "redesign API" --body "Complete rewrite for v2" --breaking -c alice
# Dry run
commit-it -t refactor -m "extract validation" --dry-run

Configuration

Works without config. When you need control, create commit.config.ts:

import{defineConfig}from'commit-it'exportdefaultdefineConfig({preset: 'conventional',// or 'gitmoji'scopeMode: 'single',// 'multi-inline' | 'multi-body'scopeMap: {'src/cli/**': 'cli','src/services/**': 'core','tests/**': 'test','*.md': 'docs',},coauthors: {alice: 'Alice Smith <alice@example.com>',bob: 'Bob Jones <bob@example.com>',},github: {enabled: true,scopeLabelPatterns: ['scope:','area:'],auto: {detectIssues: true,suggestReviewers: false},},validation: {enabled: true,maxHeaderLength: 72,requireScope: false,requireBody: false,noTrailingPeriod: true,customRules: [{name: 'no-wip',pattern: '\\bWIP\\b',message: 'WIP commits not allowed',level: 'error',invert: true,},],},})

Also supports .commitrc, .commitrc.json, .commitrc.yaml, package.json"commit" key, and more via c12.

Full config reference
OptionTypeDefaultDescription
preset'conventional' | 'gitmoji''conventional'Commit format preset
templatestringfrom presetCustom message template with {type}, {scope}, {message}
scopeMode'single' | 'multi-inline' | 'multi-body''single'How multiple scopes appear
defaults.scopestring''Pre-selected scope
defaults.includeBodybooleantrueDefault for "Add body?" prompt
scopeMapRecord<glob, scope>{}Map file patterns to scope names
coauthorsRecord<alias, 'Name <email>'>{}Co-author aliases
github.enabledbooleantrueEnable GitHub integration
github.scopeLabelPatternsstring[]['scope:', 'area:', 'component:']Label prefixes for scope extraction
github.auto.detectIssuesbooleantrueDefault to "yes" for issue prompt
github.auto.suggestReviewersbooleanfalseSuggest reviewers from collaborators
validation.enabledbooleantrueEnable validation
validation.maxHeaderLengthnumber72Max first-line length
validation.maxBodyLineLengthnumber100Max body line length (warning)
validation.requireScopebooleanfalseScope is mandatory
validation.requireBodybooleanfalseBody is mandatory
validation.requireIssuebooleanfalseIssue reference required
validation.allowedTypesstring[]from presetRestrict to these types
validation.allowedScopesstring[]anyRestrict to these scopes
validation.noTrailingPeriodbooleantrueSubject must not end with .
validation.noLeadingCapitalbooleanfalseSubject must start lowercase
validation.customRulesCustomRule[][]Regex-based custom rules

Commands

CommandDescription
commit-itCreate a commit (default)
commit-it branchCreate a branch from GitHub issues
commit-it validate -m "..."Validate a commit message
commit-it initGenerate a config file
commit-it install-hookInstall commit-msg git hook
commit-it uninstall-hookRemove the git hook
commit-it presetsList available presets
commit-it configPrint resolved config as JSON
commit-it setupRun the setup wizard
commit-it setup-aliasAdd a shell alias

Commit flags

FlagAliasDescription
--type <type>-tCommit type (feat, fix, etc.)
--message <msg>-mCommit message or full conventional string
--scope <scope>-sCommit scope
--body <text>Commit body
--all-aStage all changes before committing
--amendAmend the last commit
--breaking-bMark as breaking change
--aiGenerate message from staged diff
--no-aiDisable AI even if auto-enabled
--provider <name>AI provider (claude, codex, opencode, agent, custom)
--multiSplit changes into multiple commits
--verboseShow AI commands being run
--yes-ySkip all prompts (headless mode)
--co-author <value>-cAdd co-author by alias or "Name <email>"
--no-githubSkip GitHub API calls
--dry-runPreview without committing
--Pass flags to git commit (e.g. -- --no-verify)

Validation

Built-in rules, all configurable:

RuleDefaultDescription
Header max length72First line length limit
Conventional formatonMust follow type(scope): subject
Blank line after headeronSecond line must be blank
Body max line length100Body line length (warning)
Scope requiredoffMake scope mandatory
Body requiredoffMake body mandatory
Issue requiredoffRequire issue reference
No trailing periodonSubject can't end with .
No leading capitaloffSubject must start lowercase

Custom rules

validation: {customRules: [{name: 'require-ticket',pattern: 'PROJ-\\d+',message: 'Must include a JIRA ticket (e.g., PROJ-123)',level: 'error',invert: false,// fail when pattern DOESN'T match},{name: 'no-wip',pattern: '\\bWIP\\b',message: 'WIP commits not allowed',level: 'error',invert: true,// fail when pattern DOES match},],}

Presets

Conventional Commits (default)

Format: type(scope): message

Types: feat, fix, docs, style, refactor, perf, test, chore

Gitmoji

Format: emoji message

Types: (feature), 🐛 (fix), 📚 (docs), 💅 (style), ♻️ (refactor), (perf), (test), 🔧 (chore), 🚀 (deploy)

Programmatic API

Every feature is importable:

npm install commit-it
import{directCommit,interactiveCommit,validateCommitMessage,parseCommitMessage,generateCommitMessage,getProjectSchema,isAgentEnvironment,isAIAvailable,defineConfig,loadConfig,getPreset,GitService,GitHubService,FormatValidator,renderTemplate,buildFullMessage,}from'commit-it'
Direct commit
constresult=awaitdirectCommit({type: 'feat',message: 'add user authentication',scope: 'api',body: 'Implements OAuth with Google and GitHub providers',breaking: true,breakingDescription: 'All endpoints now require Bearer token',issueRefs: [{action: 'Closes',number: 42}],coAuthor: 'alice',stageAll: true,dryRun: false,})console.log(result.hash)// commit SHAconsole.log(result.message)// full formatted message
Interactive commit
constresult=awaitinteractiveCommit({preset: 'conventional',useAI: true,provider: 'claude',stageAll: true,headless: false,extraArgs: ['--signoff'],})
Validate and parse
// Validateconstresult=validateCommitMessage('feat(cli): add command',{maxHeaderLength: 72,requireScope: true,allowedTypes: ['feat','fix','docs'],})console.log(result.valid)// trueconsole.log(result.errors)// []// Parseconstparsed=parseCommitMessage('feat(api)!: redesign endpoints\n\nBody text\n\nCloses #42')// { type: 'feat', scope: 'api', subject: 'redesign endpoints',// body: 'Body text', isBreaking: true, issues: [42] }
Git and GitHub operations
constgit=newGitService()awaitgit.getBranchName()// "feature/new-api"awaitgit.getStatus()// { staged: [...], unstaged: [...] }awaitgit.getStagedDiff()// diff stringawaitgit.getLastCommit()// parsed commit objectconstgithub=newGitHubService()awaitgithub.searchIssues('login bug')// [{ number, title, labels }]awaitgithub.getCurrentPR()// { number, title, labels }awaitgithub.detectContext()// full branch context
AI generation
if(awaitisAIAvailable()){constsuggestion=awaitgenerateCommitMessage(diff,{branchName: 'feature/new-api',existingTypes: ['feat','fix','docs'],})// { type: 'feat', scope: 'api', message: '...', body: '...' }}
All exported types
importtype{Config,CustomRule,ValidationConfig,DirectCommitOptions,InteractiveOptions,AICommitSuggestion,CoAuthor,CommitData,CommitType,Preset,CommitOptions,CommitResult,IssueReference,ParsedCommit,CommitContext,ScopeSuggestion,TemplateData,ParsedMessage,ValidationIssue,ValidationResult,FormatValidationResult,}from'commit-it'

Example Configurations

Monorepo
exportdefaultdefineConfig({preset: 'conventional',scopeMap: {'packages/core/**': 'core','packages/cli/**': 'cli','packages/web/**': 'web','packages/api/**': 'api',},validation: {requireScope: true,allowedScopes: ['core','cli','web','api','docs','deps'],},})
Enterprise / JIRA
exportdefaultdefineConfig({preset: 'conventional',github: {enabled: false},validation: {requireScope: true,requireBody: true,allowedTypes: ['feat','fix','docs','refactor','test','chore'],customRules: [{name: 'require-jira-ticket',pattern: '[A-Z]{2,}-\\d+',message: 'Must include JIRA ticket (e.g., PROJ-123)',level: 'error',invert: false,},],},})
Security-focused
exportdefaultdefineConfig({preset: 'conventional',validation: {requireScope: true,customRules: [{name: 'no-api-keys',pattern: '\\b(api[_-]?key|api[_-]?secret)\\b',message: 'Possible API key in commit message',level: 'error',invert: true},{name: 'no-passwords',pattern: '\\b(password|passwd|pwd)\\s*[:=]',message: 'Possible password in commit message',level: 'error',invert: true},{name: 'no-tokens',pattern: '\\b(token|bearer|jwt)\\s*[:=]\\s*["\']?[A-Za-z0-9+/=]{20,}',message: 'Possible token in commit message',level: 'error',invert: true},],},})
Open source / DCO sign-off
exportdefaultdefineConfig({preset: 'conventional',github: {enabled: true,auto: {detectIssues: true,suggestReviewers: true}},validation: {customRules: [{name: 'require-signoff',pattern: 'Signed-off-by: .+ <.+@.+>',message: 'DCO sign-off required. Use: git commit -s',level: 'error',invert: false},],},})
Strict validation
exportdefaultdefineConfig({preset: 'conventional',validation: {maxHeaderLength: 50,maxBodyLineLength: 72,requireScope: true,requireBody: true,requireIssue: true,noTrailingPeriod: true,noLeadingCapital: true,allowedTypes: ['feat','fix','docs','refactor','test'],allowedScopes: ['core','api','ui','db','auth'],},})

More examples in examples/configs/.

Architecture

src/
├── cli.ts CLI entry point
├── index.ts Public API exports
├── commands/ Command implementations
├── config/ Config schema (Zod) and loading (c12)
├── presets/ Conventional and Gitmoji definitions
├── prompts/ Interactive commit flow
├── services/
│ ├── ai.ts AI generation orchestration
│ ├── ai/ Provider adapters (claude, codex, opencode, agent, custom)
│ ├── git.ts Git operations (simple-git)
│ ├── github.ts GitHub CLI wrapper
│ ├── validation.ts Rule-based validation
│ ├── scope.ts Scope suggestions
│ ├── coauthor.ts Co-author utilities
│ ├── template.ts Message templating
│ └── format.ts Format validation
└── utils/ Shared helpers

Development

bun install # install dependencies
bun run build # build
bun run dev # watch mode
bun test# run tests
bun run test:coverage # with coverage
bun run lint # check
bun run lint:fix # auto-fix
bun run type-check # type check

Troubleshooting

GitHub integration not working

Run gh auth status to check if the GitHub CLI is installed and authenticated. If not, run gh auth login. GitHub features degrade gracefully when gh is unavailable.

AI generation not working
  1. Check for a supported CLI: claude --version, codex --version, opencode --version, or agent --version
  2. Check your config: cat ~/.commit-it/config.json
  3. Make sure you have staged changes — AI generates from the staged diff
Config not loading

Run commit-it config to see the resolved config. If you see only defaults, check for syntax errors in your config file.

Validation hook not running
commit-it uninstall-hook && commit-it install-hook
ls -la .git/hooks/commit-msg # verify it exists

License

MIT

About

Standardized git commits with AI message generation, interactive prompts, validation, and GitHub integration. Works as a CLI, inside AI agents, and as a programmable TypeScript API.

Topics

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Contributors

Languages