gitpt 1.1.0 → 1.2.0

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.
package/README.md CHANGED
@@ -6,10 +6,15 @@ Git Prompt Tool is a CLI tool that helps you write commit messages using AI thro
6
6
 
7
7
  - Acts as a complete git replacement - all git commands are supported
8
8
  - Generate commit messages with AI based on your code changes
9
+
10
+ `gitpt commit`
9
11
  - Create pull requests with AI-generated titles and descriptions
12
+
13
+ `gitpt pr create`
10
14
  - Compatible with all regular git options (flags, arguments, etc.)
11
15
  - Edit suggested messages before committing
12
16
  - Works with various AI models via OpenRouter
17
+ - [Commitlint](https://commitlint.js.org/) support - read directly from your repository
13
18
 
14
19
  ## Installation
15
20
 
@@ -59,14 +64,8 @@ gitpt merge --no-ff feature-branch
59
64
  Add files to the staging area just like you would with git:
60
65
 
61
66
  ```bash
62
- # Same as git add .
67
+ # Same as git add . (supports all the regular git add options)
63
68
  gitpt add .
64
-
65
- # Same as git add -p
66
- gitpt add -p
67
-
68
- # Same as git add src/*.ts
69
- gitpt add src/*.ts
70
69
  ```
71
70
 
72
71
  ### Creating Commits
@@ -75,14 +74,22 @@ Generate an AI-powered commit message based on your staged changes:
75
74
 
76
75
  ```bash
77
76
  gitpt commit
77
+
78
+ # Or supply -m argument, if you want to avoid gitpt generating the message
79
+ gitpt commit -m "feat: file hash validation"
80
+
81
+ # Pass any other git commit options
82
+ gitpt commit --amend
78
83
  ```
79
84
 
80
85
  The tool will:
81
86
  1. Analyze your staged changes
82
87
  2. Generate a commit message using the configured AI model
83
- 3. Show you the suggested message
84
- 4. Let you edit the message before committing
85
- 5. Create the commit with your approved message
88
+ 3. Validate against commitlint rules (if configured)
89
+ 4. Regenerate the message if it fails validation
90
+ 5. Show you the suggested message
91
+ 6. Let you edit the message before committing
92
+ 7. Create the commit with your approved message
86
93
 
87
94
  ### Changing Models
88
95
 
@@ -99,20 +106,9 @@ gitpt model openai/gpt-4o
99
106
  gitpt model anthropic/claude-3-haiku
100
107
  ```
101
108
 
102
- ### Commit Options
103
-
104
- You can use any standard git commit options with the `gitpt commit` command:
105
-
106
- ```bash
107
- # Skip editing the message
108
- gitpt commit --no-edit
109
-
110
- # Provide your own message instead of generating one
111
- gitpt commit -m "Your message here"
109
+ ## GitHub Usage
112
110
 
113
- # Pass any other git commit options
114
- gitpt commit --amend
115
- ```
111
+ If you have GitHub CLI (`gh`) installed, you can use GitPT to interact with GitHub (e.g. generate full pull requests).
116
112
 
117
113
  ### Creating Pull Requests
118
114
 
@@ -145,8 +141,6 @@ gitpt pr create --no-edit
145
141
  gitpt pr create --title "Your PR title here"
146
142
  ```
147
143
 
148
- > **Note:** This command requires GitHub CLI (`gh`) to be installed and authenticated.
149
-
150
144
  ## How It Works
151
145
 
152
146
  GitPT leverages AI via OpenRouter to enhance your Git workflow while acting as a complete git wrapper:
@@ -155,6 +149,8 @@ GitPT leverages AI via OpenRouter to enhance your Git workflow while acting as a
155
149
 
156
150
  - **For commits:** Sends a diff of your staged changes to the AI, which generates a contextual commit message following best practices.
157
151
 
152
+ - **Commitlint Integration:** Automatically detects commitlint configuration files and validates generated commit messages against your project's commit conventions. If validation fails, it regenerates a compliant message.
153
+
158
154
  - **For pull requests:** Analyzes the commits and file changes between your branch and the base branch, then generates a suitable title and detailed description for your PR.
159
155
 
160
156
  - **For other git commands:** Passes them through directly to git with all arguments and options preserved, ensuring complete compatibility with your existing git workflow.
@@ -176,6 +172,20 @@ npm run build
176
172
  npm link
177
173
  ```
178
174
 
175
+ ## Commitlint Integration
176
+
177
+ GitPT automatically detects and integrates with [commitlint](https://commitlint.js.org/) if it's configured in your repository:
178
+
179
+ - **Automatic Detection:** GitPT checks for common commitlint configuration files (commitlint.config.js, .commitlintrc.*, etc.)
180
+
181
+ - **Rule-Aware Generation:** When commitlint is detected, GitPT instructs the AI to generate messages that follow your specific commit conventions
182
+
183
+ - **Validation & Regeneration:** Generated messages are validated against your commitlint rules before committing. If validation fails, GitPT automatically regenerates a compliant message
184
+
185
+ - **Error Feedback:** Validation errors are sent to the AI to help it understand how to fix the message
186
+
187
+ This integration ensures that all AI-generated commit messages follow your team's established commit conventions without requiring manual corrections.
188
+
179
189
  ## License
180
190
 
181
- MIT
191
+ MIT
@@ -3,6 +3,7 @@ import chalk from 'chalk';
3
3
  import { isGitRepository, hasStagedChanges, getStagedChanges, executeGitCommit } from '../utils/git.js';
4
4
  import { generateCommitMessage } from '../utils/api.js';
5
5
  import { getConfig } from '../utils/config.js';
6
+ import { hasCommitlintConfig, validateCommitMessage } from '../utils/commitlint.js';
6
7
  export async function commitCommand(options) {
7
8
  if (!isGitRepository()) {
8
9
  console.error(chalk.red('Error: Not a git repository'));
@@ -29,8 +30,32 @@ export async function commitCommand(options) {
29
30
  // Get staged changes
30
31
  const diff = getStagedChanges();
31
32
  console.log(chalk.blue('Generating commit message...'));
33
+ // Check if commitlint is configured
34
+ if (hasCommitlintConfig()) {
35
+ console.log(chalk.blue('Commitlint configuration detected. Generating message according to rules...'));
36
+ }
32
37
  // Generate commit message
33
38
  commitMessage = await generateCommitMessage(diff);
39
+ // If commitlint is configured, validate the message
40
+ if (hasCommitlintConfig()) {
41
+ console.log(chalk.blue('Validating commit message against commitlint rules...'));
42
+ const validation = await validateCommitMessage(commitMessage);
43
+ if (!validation.valid && validation.errors) {
44
+ console.log(chalk.yellow('Commit message failed validation. Regenerating...'));
45
+ console.log(chalk.gray(validation.errors));
46
+ // Regenerate with validation errors
47
+ commitMessage = await generateCommitMessage(diff, validation.errors);
48
+ // Validate again
49
+ const revalidation = await validateCommitMessage(commitMessage);
50
+ if (!revalidation.valid) {
51
+ console.log(chalk.yellow('Warning: Regenerated message still has validation issues.'));
52
+ console.log(chalk.gray(revalidation.errors));
53
+ }
54
+ }
55
+ else {
56
+ console.log(chalk.green('✓ Commit message passed validation'));
57
+ }
58
+ }
34
59
  console.log(chalk.green('✓ Commit message generated'));
35
60
  console.log('');
36
61
  console.log(chalk.cyan('Generated message:'));
@@ -1 +1 @@
1
- export declare function generateCommitMessage(diff: string): Promise<string>;
1
+ export declare function generateCommitMessage(diff: string, validationErrors?: string): Promise<string>;
package/dist/utils/api.js CHANGED
@@ -1,7 +1,10 @@
1
1
  import fetch from "node-fetch";
2
2
  import { getConfig } from "./config.js";
3
+ import { hasCommitlintConfig, getCommitlintRules } from "./commitlint.js";
3
4
  const OPENROUTER_API_URL = "https://openrouter.ai/api/v1/chat/completions";
4
- export async function generateCommitMessage(diff) {
5
+ export async function generateCommitMessage(diff, validationErrors) {
6
+ // Check if commitlint is configured
7
+ const hasCommitlint = hasCommitlintConfig();
5
8
  const config = getConfig();
6
9
  if (!config) {
7
10
  throw new Error('GitPT is not configured. Please run "gitpt setup" first.');
@@ -12,7 +15,7 @@ export async function generateCommitMessage(diff) {
12
15
  role: "system",
13
16
  content: `You are a helpful assistant that generates concise, informative Git commit messages.
14
17
  Follow these strict rules:
15
- 1. Use conventional commit format: type: description
18
+ ${hasCommitlint ? getCommitlintRules() : `1. Use conventional commit format: type: description
16
19
  2. Types are: feat, fix, docs, style, refactor, test, chore
17
20
  3. NO scopes in parentheses - do not use feat(scope)
18
21
  4. Keep the entire message under 100 characters
@@ -23,7 +26,7 @@ export async function generateCommitMessage(diff) {
23
26
  - feat: add user authentication
24
27
  - fix: resolve null pointer in login
25
28
  - chore: update dependencies
26
- - style: format css files`,
29
+ - style: format css files`}${validationErrors ? `\n\nYOUR PREVIOUS MESSAGE FAILED VALIDATION WITH THESE ERRORS:\n${validationErrors}\n\nFIX THESE ISSUES IN YOUR NEW MESSAGE.` : ''}`,
27
30
  },
28
31
  {
29
32
  role: "user",
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Represents a parsed commitlint configuration
3
+ */
4
+ export interface CommitlintConfig {
5
+ rules?: Record<string, any>;
6
+ extends?: string | string[];
7
+ }
8
+ /**
9
+ * Check if a commitlint configuration exists in the repository
10
+ */
11
+ export declare function hasCommitlintConfig(): boolean;
12
+ /**
13
+ * Get the commitlint configuration format rules as a string
14
+ */
15
+ export declare function getCommitlintRules(): string;
16
+ /**
17
+ * Validate a commit message against commitlint rules
18
+ * @param message The commit message to validate
19
+ * @returns An object with success status and error message if applicable
20
+ */
21
+ export declare function validateCommitMessage(message: string): Promise<{
22
+ valid: boolean;
23
+ errors?: string;
24
+ }>;
@@ -0,0 +1,124 @@
1
+ import fs from 'fs';
2
+ import { execSync } from 'child_process';
3
+ /**
4
+ * Check if a commitlint configuration exists in the repository
5
+ */
6
+ export function hasCommitlintConfig() {
7
+ const possibleConfigFiles = [
8
+ '.commitlintrc',
9
+ '.commitlintrc.json',
10
+ '.commitlintrc.yaml',
11
+ '.commitlintrc.yml',
12
+ '.commitlintrc.js',
13
+ 'commitlint.config.js',
14
+ 'package.json'
15
+ ];
16
+ // Check if any of the possible config files exist
17
+ return possibleConfigFiles.some(file => {
18
+ try {
19
+ if (file === 'package.json') {
20
+ const packageJson = JSON.parse(fs.readFileSync('package.json', 'utf8'));
21
+ return packageJson.commitlint !== undefined;
22
+ }
23
+ return fs.existsSync(file);
24
+ }
25
+ catch (error) {
26
+ return false;
27
+ }
28
+ });
29
+ }
30
+ /**
31
+ * Get the commitlint configuration format rules as a string
32
+ */
33
+ export function getCommitlintRules() {
34
+ try {
35
+ // Try to get the rules using commitlint CLI if available
36
+ try {
37
+ const rulesOutput = execSync('npx commitlint --print-config', { stdio: ['ignore', 'pipe', 'ignore'] }).toString();
38
+ const config = JSON.parse(rulesOutput);
39
+ return formatCommitlintRules(config);
40
+ }
41
+ catch (error) {
42
+ // If CLI approach fails, try to find and parse config files manually
43
+ // This is a simplified approach - in a real implementation, you'd want to handle
44
+ // all possible config files and formats
45
+ if (fs.existsSync('commitlint.config.js')) {
46
+ // We can't directly require the JS file in ESM, so we'll return a generic message
47
+ return "Follow the conventional commit format (type(scope): message)";
48
+ }
49
+ if (fs.existsSync('.commitlintrc.json')) {
50
+ const config = JSON.parse(fs.readFileSync('.commitlintrc.json', 'utf8'));
51
+ return formatCommitlintRules(config);
52
+ }
53
+ // Default to conventional commits format if we can't parse the config
54
+ return "Follow the conventional commit format (type(scope): message)";
55
+ }
56
+ }
57
+ catch (error) {
58
+ // If all else fails, return a generic message
59
+ return "Follow the conventional commit format (type(scope): message)";
60
+ }
61
+ }
62
+ /**
63
+ * Format commitlint rules into a human-readable string
64
+ */
65
+ function formatCommitlintRules(config) {
66
+ let rulesDescription = "Follow these commit message rules:\n";
67
+ if (!config.rules) {
68
+ return "Follow the conventional commit format (type(scope): message)";
69
+ }
70
+ // Extract type-enum rule if it exists
71
+ if (config.rules['type-enum'] && Array.isArray(config.rules['type-enum'][2])) {
72
+ const allowedTypes = config.rules['type-enum'][2];
73
+ rulesDescription += `- Commit type must be one of: ${allowedTypes.join(', ')}\n`;
74
+ }
75
+ // Extract other common rules
76
+ if (config.rules['scope-enum'] && Array.isArray(config.rules['scope-enum'][2])) {
77
+ const allowedScopes = config.rules['scope-enum'][2];
78
+ rulesDescription += `- Scope must be one of: ${allowedScopes.join(', ')}\n`;
79
+ }
80
+ if (config.rules['subject-case']) {
81
+ rulesDescription += `- Subject must follow case rules\n`;
82
+ }
83
+ if (config.rules['subject-max-length']) {
84
+ const maxLength = config.rules['subject-max-length'][2];
85
+ rulesDescription += `- Subject must be no longer than ${maxLength} characters\n`;
86
+ }
87
+ return rulesDescription;
88
+ }
89
+ /**
90
+ * Validate a commit message against commitlint rules
91
+ * @param message The commit message to validate
92
+ * @returns An object with success status and error message if applicable
93
+ */
94
+ export async function validateCommitMessage(message) {
95
+ if (!hasCommitlintConfig()) {
96
+ // If no commitlint config, consider it valid
97
+ return { valid: true };
98
+ }
99
+ try {
100
+ // Create a temporary file with the message
101
+ const tempFile = `/tmp/gitpt-commit-msg-${Date.now()}`;
102
+ fs.writeFileSync(tempFile, message);
103
+ try {
104
+ // Run commitlint against the file
105
+ execSync(`npx commitlint --config commitlint.config.js < ${tempFile}`, { stdio: 'ignore' });
106
+ // If we get here, validation passed
107
+ fs.unlinkSync(tempFile); // Clean up temp file
108
+ return { valid: true };
109
+ }
110
+ catch (error) {
111
+ // Capture the error output for feedback
112
+ const errorOutput = execSync(`npx commitlint --config commitlint.config.js < ${tempFile} 2>&1 || true`).toString();
113
+ fs.unlinkSync(tempFile); // Clean up temp file
114
+ return {
115
+ valid: false,
116
+ errors: errorOutput
117
+ };
118
+ }
119
+ }
120
+ catch (error) {
121
+ // If we can't run commitlint, consider it valid to avoid blocking
122
+ return { valid: true };
123
+ }
124
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gitpt",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "CLI tool that helps you write commit messages & pull request descriptions using AI",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -48,5 +48,9 @@
48
48
  "configstore": "^7.0.0",
49
49
  "inquirer": "^12.5.2",
50
50
  "node-fetch": "^3.3.2"
51
+ },
52
+ "optionalDependencies": {
53
+ "@commitlint/cli": "^18.4.3",
54
+ "@commitlint/config-conventional": "^18.4.3"
51
55
  }
52
56
  }