gitpt 1.0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 @bartaxyz
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,103 @@
1
+ # GitPT
2
+
3
+ Git Prompt Tool is a CLI tool that helps you write commit messages using AI through [OpenRouter](https://openrouter.ai/).
4
+
5
+ ## Features
6
+
7
+ - Generate commit messages with AI based on your code changes
8
+ - Compatible with all regular git commit options (you can treat it as an alias)
9
+ - Edit suggested messages before committing
10
+ - Works with various AI models via OpenRouter
11
+
12
+ ## Installation
13
+
14
+ ```bash
15
+ # Install globally
16
+ npm install -g gitpt
17
+
18
+ # Or use npx
19
+ npx gitpt
20
+ ```
21
+
22
+ ## Set up
23
+
24
+ To configure GitPT with your OpenRouter API key and select a model:
25
+
26
+ ```bash
27
+ gitpt setup
28
+ ```
29
+
30
+ This will guide you through:
31
+ 1. Entering your OpenRouter API key
32
+ 2. Selecting an AI model from popular options or specifying a custom one
33
+
34
+ You'll need an [OpenRouter](https://openrouter.ai/) account to get an API key.
35
+
36
+ ## Usage
37
+
38
+ ### Adding Files
39
+
40
+ Add files to the staging area using either git directly or the GitPT wrapper:
41
+
42
+ ```bash
43
+ # Standard git command
44
+ git add .
45
+
46
+ # Or using GitPT wrapper
47
+ gitpt add .
48
+ ```
49
+
50
+ ### Creating Commits
51
+
52
+ Generate an AI-powered commit message based on your staged changes:
53
+
54
+ ```bash
55
+ gitpt commit
56
+ ```
57
+
58
+ The tool will:
59
+ 1. Analyze your staged changes
60
+ 2. Generate a commit message using the configured AI model
61
+ 3. Show you the suggested message
62
+ 4. Let you edit the message before committing
63
+ 5. Create the commit with your approved message
64
+
65
+ ### Options
66
+
67
+ You can use any standard git commit options with the `gitpt commit` command:
68
+
69
+ ```bash
70
+ # Skip editing the message
71
+ gitpt commit --no-edit
72
+
73
+ # Provide your own message instead of generating one
74
+ gitpt commit -m "Your message here"
75
+
76
+ # Pass any other git commit options
77
+ gitpt commit --amend
78
+ ```
79
+
80
+ ## How It Works
81
+
82
+ GitPT sends a diff of your staged changes to the configured AI model via OpenRouter, which generates a contextual commit message following best practices.
83
+
84
+ ## Development
85
+
86
+ ```bash
87
+ # Clone the repository
88
+ git clone https://github.com/bartaxyz/GitPT.git
89
+ cd GitPT
90
+
91
+ # Install dependencies
92
+ npm install
93
+
94
+ # Build the project
95
+ npm run build
96
+
97
+ # Link for local development
98
+ npm link
99
+ ```
100
+
101
+ ## License
102
+
103
+ MIT
@@ -0,0 +1 @@
1
+ export declare function addCommand(files: string[]): void;
@@ -0,0 +1,16 @@
1
+ import chalk from 'chalk';
2
+ import { isGitRepository, executeGitAdd } from '../utils/git.js';
3
+ export function addCommand(files) {
4
+ if (!isGitRepository()) {
5
+ console.error(chalk.red('Error: Not a git repository'));
6
+ process.exit(1);
7
+ }
8
+ try {
9
+ executeGitAdd(files);
10
+ console.log(chalk.green('Files added to staging area'));
11
+ }
12
+ catch (error) {
13
+ console.error(chalk.red('Error:'), error instanceof Error ? error.message : String(error));
14
+ process.exit(1);
15
+ }
16
+ }
@@ -0,0 +1,7 @@
1
+ interface CommitOptions {
2
+ message?: string;
3
+ edit?: boolean;
4
+ [key: string]: any;
5
+ }
6
+ export declare function commitCommand(options: CommitOptions): Promise<void>;
7
+ export {};
@@ -0,0 +1,74 @@
1
+ import inquirer from 'inquirer';
2
+ import chalk from 'chalk';
3
+ import { isGitRepository, hasStagedChanges, getStagedChanges, executeGitCommit } from '../utils/git.js';
4
+ import { generateCommitMessage } from '../utils/api.js';
5
+ import { getConfig } from '../utils/config.js';
6
+ export async function commitCommand(options) {
7
+ if (!isGitRepository()) {
8
+ console.error(chalk.red('Error: Not a git repository'));
9
+ process.exit(1);
10
+ }
11
+ // Check if config exists
12
+ const config = getConfig();
13
+ if (!config) {
14
+ console.error(chalk.red('GitPT is not configured. Please run "gitpt setup" first.'));
15
+ process.exit(1);
16
+ }
17
+ // Check if there are staged changes
18
+ if (!hasStagedChanges()) {
19
+ console.error(chalk.yellow('No staged changes. Use "git add" or "gitpt add" to stage changes first.'));
20
+ process.exit(1);
21
+ }
22
+ let commitMessage;
23
+ // If message is provided, use that
24
+ if (options.message) {
25
+ commitMessage = options.message;
26
+ }
27
+ else {
28
+ try {
29
+ // Get staged changes
30
+ const diff = getStagedChanges();
31
+ console.log(chalk.blue('Generating commit message...'));
32
+ // Generate commit message
33
+ commitMessage = await generateCommitMessage(diff);
34
+ console.log(chalk.green('✓ Commit message generated'));
35
+ console.log('');
36
+ console.log(chalk.cyan('Generated message:'));
37
+ console.log(commitMessage);
38
+ console.log('');
39
+ }
40
+ catch (error) {
41
+ console.error(chalk.red('Error:'), error instanceof Error ? error.message : String(error));
42
+ process.exit(1);
43
+ }
44
+ }
45
+ // If edit is true or not specified, prompt user to edit the message
46
+ if (options.edit !== false) {
47
+ const answer = await inquirer.prompt([
48
+ {
49
+ type: 'editor',
50
+ name: 'message',
51
+ message: 'Edit commit message:',
52
+ default: commitMessage
53
+ }
54
+ ]);
55
+ commitMessage = answer.message;
56
+ }
57
+ // Extract other git options to pass through
58
+ const gitOptions = Object.keys(options)
59
+ .filter(key => !['message', 'edit'].includes(key))
60
+ .map(key => {
61
+ if (typeof options[key] === 'boolean') {
62
+ return options[key] ? `--${key}` : `--no-${key}`;
63
+ }
64
+ return `--${key}=${options[key]}`;
65
+ });
66
+ try {
67
+ executeGitCommit(commitMessage, gitOptions);
68
+ console.log(chalk.green('✓ Changes committed successfully'));
69
+ }
70
+ catch (error) {
71
+ console.error(chalk.red('Error:'), error instanceof Error ? error.message : String(error));
72
+ process.exit(1);
73
+ }
74
+ }
@@ -0,0 +1 @@
1
+ export declare function setupCommand(): Promise<void>;
@@ -0,0 +1,60 @@
1
+ import inquirer from 'inquirer';
2
+ import chalk from 'chalk';
3
+ import { saveConfig, clearConfig } from '../utils/config.js';
4
+ // List of popular models available on OpenRouter
5
+ const POPULAR_MODELS = [
6
+ { name: 'Claude 3 Opus - Anthropic', value: 'anthropic/claude-3-opus:beta' },
7
+ { name: 'Claude 3 Sonnet - Anthropic', value: 'anthropic/claude-3-sonnet:beta' },
8
+ { name: 'Claude 3 Haiku - Anthropic', value: 'anthropic/claude-3-haiku:beta' },
9
+ { name: 'GPT-4o - OpenAI', value: 'openai/gpt-4o' },
10
+ { name: 'GPT-4 Turbo - OpenAI', value: 'openai/gpt-4-turbo' },
11
+ { name: 'GPT-3.5 Turbo - OpenAI', value: 'openai/gpt-3.5-turbo' },
12
+ { name: 'Other (specify model identifier)', value: 'custom' }
13
+ ];
14
+ export async function setupCommand() {
15
+ console.log(chalk.blue('GitPT Setup'));
16
+ console.log('This will configure GitPT to use OpenRouter for generating commit messages.');
17
+ console.log('If you don\'t have an OpenRouter account yet, sign up at https://openrouter.ai');
18
+ console.log('');
19
+ const answers = await inquirer.prompt([
20
+ {
21
+ type: 'input',
22
+ name: 'apiKey',
23
+ message: 'Enter your OpenRouter API key:',
24
+ validate: (input) => {
25
+ if (!input)
26
+ return 'API key is required';
27
+ return true;
28
+ }
29
+ },
30
+ {
31
+ type: 'list',
32
+ name: 'modelChoice',
33
+ message: 'Select an AI model:',
34
+ choices: POPULAR_MODELS
35
+ },
36
+ {
37
+ type: 'input',
38
+ name: 'customModel',
39
+ message: 'Enter model identifier:',
40
+ when: (answers) => answers.modelChoice === 'custom',
41
+ validate: (input) => {
42
+ if (!input)
43
+ return 'Model identifier is required';
44
+ return true;
45
+ }
46
+ }
47
+ ]);
48
+ // Clear existing config before saving new one
49
+ clearConfig();
50
+ // Save the new configuration
51
+ const model = answers.modelChoice === 'custom' ? answers.customModel : answers.modelChoice;
52
+ saveConfig({
53
+ apiKey: answers.apiKey,
54
+ model: model
55
+ });
56
+ console.log(chalk.green('✓ GitPT configuration saved successfully'));
57
+ console.log(`Model set to: ${chalk.yellow(model)}`);
58
+ console.log('');
59
+ console.log(`Use ${chalk.cyan('gitpt commit')} to create commits with AI-generated messages.`);
60
+ }
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,48 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from 'commander';
3
+ import chalk from 'chalk';
4
+ import { setupCommand } from './commands/setup.js';
5
+ import { commitCommand } from './commands/commit.js';
6
+ import { addCommand } from './commands/add.js';
7
+ import fs from 'fs';
8
+ import path from 'path';
9
+ import { fileURLToPath } from 'url';
10
+ const __filename = fileURLToPath(import.meta.url);
11
+ const __dirname = path.dirname(__filename);
12
+ const packageJson = JSON.parse(fs.readFileSync(path.join(__dirname, '../package.json'), 'utf8'));
13
+ const version = packageJson.version;
14
+ const program = new Command();
15
+ program
16
+ .name('gitpt')
17
+ .description('Git Prompt Tool helps you write commit messages using AI')
18
+ .version(version);
19
+ // Setup command
20
+ program
21
+ .command('setup')
22
+ .description('Configure GitPT with your OpenRouter API key and model selection')
23
+ .action(setupCommand);
24
+ // Add command (pass-through to git add)
25
+ program
26
+ .command('add [files...]')
27
+ .description('Add files to git staging area (pass-through to git add)')
28
+ .action(addCommand);
29
+ // Commit command
30
+ program
31
+ .command('commit')
32
+ .description('Generate AI-powered commit message based on staged changes')
33
+ .option('-m, --message <message>', 'use provided message instead of generating one')
34
+ .option('-e, --edit', 'edit the message after generation')
35
+ .option('--no-edit', 'do not edit the message after generation')
36
+ .allowUnknownOption(true) // Pass through other git commit options
37
+ .action(commitCommand);
38
+ // Main logic
39
+ async function main() {
40
+ try {
41
+ await program.parseAsync();
42
+ }
43
+ catch (error) {
44
+ console.error(chalk.red('Error:'), error instanceof Error ? error.message : String(error));
45
+ process.exit(1);
46
+ }
47
+ }
48
+ main();
@@ -0,0 +1 @@
1
+ export declare function generateCommitMessage(diff: string): Promise<string>;
@@ -0,0 +1,55 @@
1
+ import fetch from 'node-fetch';
2
+ import { getConfig } from './config.js';
3
+ const OPENROUTER_API_URL = 'https://openrouter.ai/api/v1/chat/completions';
4
+ export async function generateCommitMessage(diff) {
5
+ const config = getConfig();
6
+ if (!config) {
7
+ throw new Error('GitPT is not configured. Please run "gitpt setup" first.');
8
+ }
9
+ const { apiKey, model } = config;
10
+ const messages = [
11
+ {
12
+ role: 'system',
13
+ content: `You are a helpful assistant that generates concise, informative Git commit messages.
14
+ Follow conventional commit style. Be brief but descriptive.
15
+ Focus on WHAT changes were made, WHY they were made, and their IMPACT.
16
+ Use present tense (e.g., "add feature" not "added feature").
17
+ Split the message like this if needed:
18
+
19
+ feat(scope): short description
20
+
21
+ More detailed explanation if necessary
22
+
23
+ Only include a detailed explanation for complex changes.`
24
+ },
25
+ {
26
+ role: 'user',
27
+ content: `Generate a commit message for the following git diff:\n\n${diff}`
28
+ }
29
+ ];
30
+ try {
31
+ const response = await fetch(OPENROUTER_API_URL, {
32
+ method: 'POST',
33
+ headers: {
34
+ 'Content-Type': 'application/json',
35
+ 'Authorization': `Bearer ${apiKey}`,
36
+ 'HTTP-Referer': 'https://github.com/bartaxyz/GitPT',
37
+ },
38
+ body: JSON.stringify({
39
+ model: model,
40
+ messages: messages,
41
+ max_tokens: 300,
42
+ }),
43
+ });
44
+ if (!response.ok) {
45
+ const errorText = await response.text();
46
+ throw new Error(`API request failed: ${response.status} ${response.statusText}\n${errorText}`);
47
+ }
48
+ const data = await response.json();
49
+ return data.choices[0].message.content.trim();
50
+ }
51
+ catch (error) {
52
+ console.error('Error generating commit message:', error);
53
+ throw new Error('Failed to generate commit message');
54
+ }
55
+ }
@@ -0,0 +1,7 @@
1
+ export interface GitPTConfig {
2
+ apiKey: string;
3
+ model: string;
4
+ }
5
+ export declare function getConfig(): GitPTConfig | null;
6
+ export declare function saveConfig(newConfig: GitPTConfig): void;
7
+ export declare function clearConfig(): void;
@@ -0,0 +1,24 @@
1
+ import Configstore from 'configstore';
2
+ import chalk from 'chalk';
3
+ const config = new Configstore('gitpt');
4
+ export function getConfig() {
5
+ try {
6
+ const apiKey = config.get('apiKey');
7
+ const model = config.get('model');
8
+ if (!apiKey || !model) {
9
+ return null;
10
+ }
11
+ return { apiKey, model };
12
+ }
13
+ catch (error) {
14
+ console.error(chalk.red('Error reading configuration:'), error);
15
+ return null;
16
+ }
17
+ }
18
+ export function saveConfig(newConfig) {
19
+ config.set('apiKey', newConfig.apiKey);
20
+ config.set('model', newConfig.model);
21
+ }
22
+ export function clearConfig() {
23
+ config.clear();
24
+ }
@@ -0,0 +1,6 @@
1
+ export declare function isGitRepository(): boolean;
2
+ export declare function getStagedChanges(): string;
3
+ export declare function getStagedFiles(): string[];
4
+ export declare function hasStagedChanges(): boolean;
5
+ export declare function executeGitAdd(files: string[]): void;
6
+ export declare function executeGitCommit(message: string, additionalArgs?: string[]): void;
@@ -0,0 +1,62 @@
1
+ import { execSync } from 'child_process';
2
+ import chalk from 'chalk';
3
+ export function isGitRepository() {
4
+ try {
5
+ execSync('git rev-parse --is-inside-work-tree', { stdio: 'ignore' });
6
+ return true;
7
+ }
8
+ catch (error) {
9
+ return false;
10
+ }
11
+ }
12
+ export function getStagedChanges() {
13
+ try {
14
+ return execSync('git diff --staged').toString();
15
+ }
16
+ catch (error) {
17
+ console.error(chalk.red('Error getting staged changes:'), error);
18
+ throw new Error('Failed to get staged changes');
19
+ }
20
+ }
21
+ export function getStagedFiles() {
22
+ try {
23
+ const result = execSync('git diff --staged --name-only').toString();
24
+ return result.split('\n').filter(Boolean);
25
+ }
26
+ catch (error) {
27
+ console.error(chalk.red('Error getting staged files:'), error);
28
+ throw new Error('Failed to get staged files');
29
+ }
30
+ }
31
+ export function hasStagedChanges() {
32
+ try {
33
+ const output = execSync('git diff --staged --quiet || echo "has-changes"').toString();
34
+ return output.includes('has-changes');
35
+ }
36
+ catch (error) {
37
+ return true; // Assume there are changes if we can't check
38
+ }
39
+ }
40
+ export function executeGitAdd(files) {
41
+ try {
42
+ if (files.length === 0) {
43
+ throw new Error('No files specified');
44
+ }
45
+ const fileArgs = files.join(' ');
46
+ execSync(`git add ${fileArgs}`, { stdio: 'inherit' });
47
+ }
48
+ catch (error) {
49
+ console.error(chalk.red('Error adding files:'), error);
50
+ throw new Error('Failed to add files to git');
51
+ }
52
+ }
53
+ export function executeGitCommit(message, additionalArgs = []) {
54
+ try {
55
+ const args = additionalArgs.join(' ');
56
+ execSync(`git commit -m "${message}" ${args}`, { stdio: 'inherit' });
57
+ }
58
+ catch (error) {
59
+ console.error(chalk.red('Error committing changes:'), error);
60
+ throw new Error('Failed to commit changes');
61
+ }
62
+ }
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "gitpt",
3
+ "version": "1.0.0",
4
+ "description": "CLI tool that helps you write commit messages & pull request descriptions using AI",
5
+ "main": "dist/index.js",
6
+ "type": "module",
7
+ "bin": {
8
+ "gitpt": "dist/index.js"
9
+ },
10
+ "files": [
11
+ "dist/**/*",
12
+ "README.md",
13
+ "LICENSE"
14
+ ],
15
+ "scripts": {
16
+ "build": "tsc",
17
+ "dev": "ts-node --esm src/index.ts",
18
+ "start": "node dist/index.js",
19
+ "test": "echo \"Error: no test specified\" && exit 1",
20
+ "prepublishOnly": "npm run build"
21
+ },
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "git+https://github.com/bartaxyz/GitPT.git"
25
+ },
26
+ "keywords": [
27
+ "git",
28
+ "ai",
29
+ "prompt"
30
+ ],
31
+ "author": "@bartaxyz",
32
+ "license": "MIT",
33
+ "bugs": {
34
+ "url": "https://github.com/bartaxyz/GitPT/issues"
35
+ },
36
+ "homepage": "https://github.com/bartaxyz/GitPT#readme",
37
+ "devDependencies": {
38
+ "@types/configstore": "^6.0.2",
39
+ "@types/inquirer": "^9.0.7",
40
+ "@types/node": "^22.14.1",
41
+ "@types/node-fetch": "^2.6.12",
42
+ "ts-node": "^10.9.2",
43
+ "typescript": "^5.8.3"
44
+ },
45
+ "dependencies": {
46
+ "chalk": "^5.4.1",
47
+ "commander": "^13.1.0",
48
+ "configstore": "^7.0.0",
49
+ "inquirer": "^12.5.2",
50
+ "node-fetch": "^3.3.2"
51
+ }
52
+ }