mcpField
C
commitlint

commitlint

github.com/jamie-bitflightDevelopment35 installs
Repository

About commitlint

A procedural skill for configuring and using commitlint to check commit messages against Conventional Commits rules. It covers rule setup, configuration discovery, command-line and programmatic validation, and integration with hooks, CI/CD, and LLM-based message generation.

Key features

  1. 01Discover commitlint configuration files
  2. 02Configure Conventional Commits rules
  3. 03Validate messages via CLI or code
  4. 04Integrate checks with hooks and CI/CD
  5. 05Extract rules for LLM prompts

Use cases

  1. 01Set up commit message checks in a repository
  2. 02Validate commits in a CI/CD pipeline
  3. 03Generate commit messages from diffs and rules
  4. 04Troubleshoot commitlint configuration errors

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/jamie-bitflight/claude_skills --skill commitlint

After installing, type/commitlintin Claude Code to confirm it loaded.

Details

From the project README

Commitlint

Validate commit messages against Conventional Commits format using commitlint configuration and rules.

When to Use This Skill

Use this skill when:

  • Setting up commitlint for a repository
  • Configuring commitlint rules and shareable configurations
  • Integrating commitlint with pre-commit hooks or CI/CD pipelines
  • Extracting rules from commitlint config to generate LLM prompts for commit message generation
  • Validating commit messages programmatically
  • Troubleshooting commitlint configuration or validation errors
  • Understanding commitlint rule syntax and severity levels
  • Detecting commitlint configuration files in a repository

Core Capabilities

Configuration Detection

Commitlint uses cosmiconfig to find configuration files in this priority order:

Dedicated config files:

.commitlintrc
.commitlintrc.json
.commitlintrc.yaml
.commitlintrc.yml
.commitlintrc.js
.commitlintrc.cjs
.commitlintrc.mjs
.commitlintrc.ts
.commitlintrc.cts
commitlint.config.js
commitlint.config.cjs
commitlint.config.mjs
commitlint.config.ts
commitlint.config.cts

Package files:

  • package.json with commitlint field
  • package.yaml (PNPM) with commitlint field
Configuration Formats

JavaScript ES Modules (Recommended):

// commitlint.config.js or commitlint.config.mjs
export default {
  extends: ['@commitlint/config-conventional'],
};

TypeScript:

// commitlint.config.ts
import type { UserConfig } from '@commitlint/types';
import { RuleConfigSeverity } from '@commitlint/types';

const config: UserConfig = {
  extends: ['@commitlint/config-conventional'],
  rules: {
    'type-enum': [RuleConfigSeverity.Error, 'always', ['feat', 'fix', 'docs']],
  },
};

export default config;

JSON:

{
  "extends": ["@commitlint/config-conventional"]
}

YAML:

extends:
  - "@commitlint/config-conventional"
Rule Configuration

Rules are configured as arrays: [level, applicability, value]

Severity Levels:

LevelMeaningTypeScript Enum
0DisabledRuleConfigSeverity.Disabled
1WarningRuleConfigSeverity.Warning
2ErrorRuleConfigSeverity.Error

Applicability:

  • 'always' - Rule must match
  • 'never' - Rule must not match

Example rules:

rules: {
  // Error if type is not in enum
  'type-enum': [2, 'always', ['feat', 'fix', 'docs', 'style', 'refactor', 'perf', 'test', 'build', 'ci', 'chore', 'revert']],

  // Error if type is empty
  'type-empty': [2, 'never'],

  // Error if subject is empty
  'subject-empty': [2, 'never'],

  // Error if header exceeds 100 chars
  'header-max-length': [2, 'always', 100],

  // Error if subject ends with period
  'subject-full-stop': [2, 'never', '.'],

  // Warning if body doesn't have leading blank line
  'body-leading-blank': [1, 'always'],
}

Common Configurations

@commitlint/config-conventional

The most widely used shareable configuration. Default error-level rules:

RuleConfigurationPass ExampleFail Example
type-enum['build', 'chore', 'ci', 'docs', 'feat', 'fix', 'perf', 'refactor', 'revert', 'style', 'test']fix: messagefoo: message
type-case'lowerCase'fix: messageFIX: message
type-emptyneverfix: message: message
subject-casenever + ['sentence-case', 'start-case', 'pascal-case', 'upper-case']fix: some messagefix: Some Message
subject-emptyneverfix: messagefix:
subject-full-stopnever, '.'fix: messagefix: message.
header-max-length100Short headerHeader > 100 chars
body-leading-blankalways (warning)Blank line before bodyNo blank line
body-max-line-length100Lines <= 100 charsLine > 100 chars
footer-leading-blankalways (warning)Blank line before footerNo blank line
footer-max-line-length100Lines <= 100 charsLine > 100 chars
Complete Configuration Schema
// commitlint.config.js
export default {
  // Extend shareable configs (resolved via node resolution)
  extends: ['@commitlint/config-conventional'],

  // Parser preset for parsing commit messages
  parserPreset: 'conventional-changelog-atom',

  // Output formatter
  formatter: '@commitlint/format',

  // Custom rules (override inherited rules)
  rules: {
    'type-enum': [2, 'always', ['feat', 'fix', 'docs', 'style', 'refactor', 'test', 'chore']],
  },

  // Functions that return true to ignore specific commits
  // Merged with default ignores (merge commits, reverts, semver tags)
  ignores: [(commit) => commit.includes('WIP')],

  // Whether to use default ignore patterns
  // Default patterns: 'Merge pull request', 'Revert X', 'v1.2.3', etc.
  defaultIgnores: true,

  // Custom help URL shown on failure
  helpUrl: 'https://example.com/commit-guidelines',

  // Prompt configuration (for @commitlint/cz-commitlint)
  prompt: {
    messages: {},
    questions: {
      type: {
        description: 'Select the type of change:',
      },
    },
  },
};

CLI Usage

Installation
# npm
npm install -D @commitlint/cli @commitlint/config-conventional

# yarn
yarn add -D @commitlint/cli @commitlint/config-conventional

# pnpm
pnpm add -D @commitlint/cli @commitlint/config-conventional
Common Commands
# Lint the last commit
npx commitlint --last

# Lint a range of commits
npx commitlint --from HEAD~5

# Lint from a specific commit
npx commitlint --from abc1234

# Lint message from stdin
echo "feat: add feature" | npx commitlint

# Lint message from file
npx commitlint < .git/COMMIT_EDITMSG

# Lint with edit flag (reads .git/COMMIT_EDITMSG)
npx commitlint --edit

# Lint with custom config path
npx commitlint --config ./custom-commitlint.config.js

# Print resolved config
npx commitlint --print-config

# Strict mode (warnings become exit code 2)
npx commitlint --last --strict
Exit Codes
CodeMeaning
0Success (no errors)
1Lint errors found
2Warnings found (strict mode only)
3Errors found (strict mode)
9Config file missing (with -g flag)

Rule Reference

Type Rules
RuleConditionDefault Applicability
type-enumtype is found in valuealways
type-casetype is in case valuealways, 'lower-case'
type-emptytype is emptynever
type-max-lengthtype has value or less charactersalways, Infinity
type-min-lengthtype has value or more charactersalways, 0
Subject Rules
RuleConditionDefault Applicability
subject-casesubject is in case valuealways
subject-emptysubject is emptynever
subject-full-stopsubject ends with valuenever, '.'
subject-max-lengthsubject has value or less charactersalways, Infinity
subject-min-lengthsubject has value or more charactersalways, 0
subject-exclamation-marksubject has exclamation before :never
Scope Rules
RuleConditionDefault Applicability
scope-enumscope is found in valuealways, []
scope-casescope is in case valuealways, 'lower-case'
scope-emptyscope is emptynever
scope-max-lengthscope has value or less charactersalways, Infinity
scope-min-lengthscope has value or more charactersalways, 0
Header Rules
RuleConditionDefault Applicability
header-caseheader is in case valuealways, 'lower-case'
header-full-stopheader ends with valuenever, '.'
header-max-lengthheader has value or less charactersalways, 72
header-min-lengthheader has value or more charactersalways, 0
header-trimheader has no leading/trailing whitespacealways
Body Rules
RuleConditionDefault Applicability
body-leading-blankbody begins with blank linealways
body-emptybody is emptynever
body-max-lengthbody has value or less charactersalways, Infinity
body-max-line-lengthbody lines have value or less characters (URLs excluded)always, Infinity
body-min-lengthbody has value or more charactersalways, 0
body-casebody is in case valuealways, 'lower-case'
body-full-stopbody ends with valuenever, '.'
Footer Rules
RuleConditionDefault Applicability
footer-leading-blankfooter begins with blank linealways
footer-emptyfooter is emptynever
footer-max-lengthfooter has value or less charactersalways, Infinity
footer-max-line-lengthfooter lines have value or less charactersalways, Infinity
footer-min-lengthfooter has value or more charactersalways, 0
Case Values

For rules that check case (*-case):

[
  'lower-case',    // lowercase
  'upper-case',    // UPPERCASE
  'camel-case',    // camelCase
  'kebab-case',    // kebab-case
  'pascal-case',   // PascalCase
  'sentence-case', // Sentence case
  'snake-case',    // snake_case
  'start-case',    // Start Case
]

Programmatic Usage

Load Configuration
import load from '@commitlint/load';

async function getCommitlintConfig() {
  const config = await load();
  console.log(config.rules);
  return config;
}
Validate Message
import load from '@commitlint/load';
import lint from '@commitlint/lint';

async function validateMessage(message) {
  const config = await load();
  const result = await lint(message, config.rules);

  return {
    valid: result.valid,
    errors: result.errors,
    warnings: result.warnings,
  };
}

LLM Integration Patterns

Extract Rules for Prompt Generation

Generate LLM-friendly constraints from commitlint config:

def extract_rules_for_prompt(config: dict) -> str:
    """Extract commitlint rules into LLM-friendly format."""
    rules = config.get("rules", {})
    prompt_parts = []

    # Extract type-enum if present
    if "type-enum" in rules:
        level, applicability, types = rules["type-enum"]
        if level > 0 and applicability == "always":
            prompt_parts.append(f"Allowed commit types: {', '.join(types)}")

    # Extract scope-enum if present
    if "scope-enum" in rules:
        level, applicability, scopes = rules["scope-enum"]
        if level > 0 and applicability == "always" and scopes:
            prompt_parts.append(f"Allowed scopes: {', '.join(scopes)}")

    # Extract header-max-length
    if "header-max-length" in rules:
        level, applicability, length = rules["header-max-length"]
        if level > 0:
            prompt_parts.append(f"Header must be {length} characters or less")

    # Extract subject-case
    if "subject-case" in rules:
        level, applicability, cases = rules["subject-case"]
        if level > 0 and applicability == "never":
            prompt_parts.append(f"Subject must NOT use: {', '.join(cases)}")

    return "\n".join(prompt_parts)
Validation Loop
import subprocess


async def validate_with_commitlint(message: str, cwd: Path | None = None) -> tuple[bool, list[str]]:
    """
    Validate commit message with commitlint.

    Args:
        message: The commit message to validate
        cwd: Working directory (defaults to current directory)

    Returns:
        Tuple of (is_valid, error_messages)
    """
    result = subprocess.run(["npx", "commitlint"], input=message, capture_output=True, text=True, cwd=cwd)

    if result.returncode == 0:
        return True, []

    # Parse errors from stderr
    errors = [line.strip() for line in result.stderr.split("\n") if line.strip()]
    return False, errors

Validation loop pattern:

  1. LLM generates commit message based on diff and rules
  2. Run commitlint on generated message
  3. If validation fails, feed errors back to LLM with context
  4. Retry (max 3 times by default)
  5. Return final message (valid or best effort after retries)

Common Issues

Node v24 ESM issues:

Use .mjs extension for ES modules config, or add "type": "module" to package.json

Missing extends:

Config without extends or rules fails with "Please add rules" error. Include at least one:

export default {
  extends: ['@commitlint/config-conventional'],
};

Empty config error:

Config file must have at least extends or rules defined.

Scope enum empty array:

scope-enum with [] passes all scopes. Use specific array to restrict:

rules: {
  'scope-enum': [2, 'always', ['api', 'ui', 'docs']],
}

Subject case trap:

@commitlint/config-conventional uses never with specific cases, meaning those cases are forbidden (not required):

// This forbids sentence-case, start-case, pascal-case, upper-case
'subject-case': [2, 'never', ['sentence-case', 'start-case', 'pascal-case', 'upper-case']]

Pre-commit Integration

For pre-commit hook integration with commitlint, configure your hook manager directly. If your workspace includes an installed pre-commit plugin, you can use that for hook setup guidance.

Conventional Commits Reference

For Conventional Commits format specification and examples, use the reference in this document. If your workspace includes an installed Conventional Commits plugin, you can defer to it for additional examples and messaging patterns.

References

Official Documentation
Related Specifications

Comments

Markdown supported