gitpt 1.2.0 → 1.3.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 +20 -10
- package/dist/commands/commit/context/systemPrompt.d.ts +1 -0
- package/dist/commands/commit/context/systemPrompt.js +38 -0
- package/dist/commands/commit/context/userPrompt.d.ts +1 -0
- package/dist/commands/commit/context/userPrompt.js +5 -0
- package/dist/commands/commit/generateCommitMessage.d.ts +1 -0
- package/dist/commands/commit/generateCommitMessage.js +39 -0
- package/dist/commands/{commit.d.ts → commit/index.d.ts} +1 -1
- package/dist/commands/commit/index.js +183 -0
- package/dist/commands/config.d.ts +1 -0
- package/dist/commands/config.js +11 -0
- package/dist/commands/middleware/capabilitiesMiddleware/ghCapability.d.ts +1 -0
- package/dist/commands/middleware/capabilitiesMiddleware/ghCapability.js +23 -0
- package/dist/commands/middleware/capabilitiesMiddleware/gitCapability.d.ts +1 -0
- package/dist/commands/middleware/capabilitiesMiddleware/gitCapability.js +12 -0
- package/dist/commands/middleware/capabilitiesMiddleware/index.d.ts +3 -0
- package/dist/commands/middleware/capabilitiesMiddleware/index.js +15 -0
- package/dist/commands/middleware/hasStagedChangesMiddleware.d.ts +1 -0
- package/dist/commands/middleware/hasStagedChangesMiddleware.js +8 -0
- package/dist/commands/middleware/setupMiddleware/getAvailableModels.d.ts +4 -0
- package/dist/commands/middleware/setupMiddleware/getAvailableModels.js +11 -0
- package/dist/commands/middleware/setupMiddleware/getOrUpdateApiKey.d.ts +1 -0
- package/dist/commands/middleware/setupMiddleware/getOrUpdateApiKey.js +39 -0
- package/dist/commands/middleware/setupMiddleware/index.d.ts +4 -0
- package/dist/commands/middleware/setupMiddleware/index.js +40 -0
- package/dist/commands/middleware/setupMiddleware/selectModel.d.ts +5 -0
- package/dist/commands/middleware/setupMiddleware/selectModel.js +38 -0
- package/dist/commands/middleware/setupMiddleware/setupLocalLLM.d.ts +5 -0
- package/dist/commands/middleware/setupMiddleware/setupLocalLLM.js +60 -0
- package/dist/commands/middleware/setupMiddleware/setupOpenRouter.d.ts +2 -0
- package/dist/commands/middleware/setupMiddleware/setupOpenRouter.js +66 -0
- package/dist/commands/middleware/setupMiddleware/types.d.ts +13 -0
- package/dist/commands/middleware/setupMiddleware/types.js +1 -0
- package/dist/commands/model.d.ts +1 -1
- package/dist/commands/model.js +6 -114
- package/dist/commands/pr/context/systemPrompt.d.ts +1 -0
- package/dist/commands/pr/context/systemPrompt.js +18 -0
- package/dist/commands/pr/context/userPrompt.d.ts +1 -0
- package/dist/commands/pr/context/userPrompt.js +20 -0
- package/dist/commands/pr/generatePRDetails.d.ts +4 -0
- package/dist/commands/pr/generatePRDetails.js +35 -0
- package/dist/commands/pr/getPRContext.d.ts +1 -0
- package/dist/commands/pr/getPRContext.js +65 -0
- package/dist/commands/{pr.d.ts → pr/index.d.ts} +1 -1
- package/dist/commands/pr/index.js +66 -0
- package/dist/commands/setup.d.ts +3 -1
- package/dist/commands/setup.js +15 -60
- package/dist/config.d.ts +18 -0
- package/dist/config.js +58 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +37 -49
- package/dist/llm/index.d.ts +5 -0
- package/dist/llm/index.js +14 -0
- package/dist/services/gh/createPullRequest.d.ts +1 -0
- package/dist/services/gh/createPullRequest.js +63 -0
- package/dist/services/gh/index.d.ts +4 -0
- package/dist/services/gh/index.js +6 -0
- package/dist/services/gh/isAvailable.d.ts +1 -0
- package/dist/services/gh/isAvailable.js +10 -0
- package/dist/services/git/executeGitAdd.d.ts +1 -0
- package/dist/services/git/executeGitAdd.js +15 -0
- package/dist/services/git/executeGitCommit.d.ts +1 -0
- package/dist/services/git/executeGitCommit.js +12 -0
- package/dist/services/git/getChangedFiles.d.ts +1 -0
- package/dist/services/git/getChangedFiles.js +64 -0
- package/dist/services/git/getCommitsSinceBaseBranch.d.ts +1 -0
- package/dist/services/git/getCommitsSinceBaseBranch.js +59 -0
- package/dist/services/git/getCurrentBranch.d.ts +1 -0
- package/dist/services/git/getCurrentBranch.js +11 -0
- package/dist/services/git/getDefaultBranch.d.ts +1 -0
- package/dist/services/git/getDefaultBranch.js +63 -0
- package/dist/services/git/getStagedChanges.d.ts +1 -0
- package/dist/services/git/getStagedChanges.js +11 -0
- package/dist/services/git/getStagedFiles.d.ts +1 -0
- package/dist/services/git/getStagedFiles.js +12 -0
- package/dist/services/git/hasStagedChanges.d.ts +1 -0
- package/dist/services/git/hasStagedChanges.js +10 -0
- package/dist/services/git/index.d.ts +13 -0
- package/dist/services/git/index.js +24 -0
- package/dist/services/git/isAvailable.d.ts +1 -0
- package/dist/services/git/isAvailable.js +10 -0
- package/dist/services/git/isGitRepository.d.ts +1 -0
- package/dist/services/git/isGitRepository.js +10 -0
- package/dist/utils/commitlint.d.ts +4 -3
- package/dist/utils/commitlint.js +62 -38
- package/dist/utils/formatBaseURL.d.ts +1 -0
- package/dist/utils/formatBaseURL.js +7 -0
- package/dist/utils/maskApiKey.d.ts +1 -0
- package/dist/utils/maskApiKey.js +8 -0
- package/package.json +9 -10
- package/dist/commands/add.d.ts +0 -1
- package/dist/commands/add.js +0 -16
- package/dist/commands/commit.js +0 -99
- package/dist/commands/pr.js +0 -458
- package/dist/utils/api.d.ts +0 -1
- package/dist/utils/api.js +0 -61
- package/dist/utils/config.d.ts +0 -7
- package/dist/utils/config.js +0 -24
- package/dist/utils/git.d.ts +0 -6
- package/dist/utils/git.js +0 -62
package/dist/commands/pr.js
DELETED
|
@@ -1,458 +0,0 @@
|
|
|
1
|
-
import inquirer from 'inquirer';
|
|
2
|
-
import chalk from 'chalk';
|
|
3
|
-
import { execSync } from 'child_process';
|
|
4
|
-
import { isGitRepository } from '../utils/git.js';
|
|
5
|
-
import { getConfig } from '../utils/config.js';
|
|
6
|
-
import fetch from 'node-fetch';
|
|
7
|
-
function getCurrentBranch() {
|
|
8
|
-
try {
|
|
9
|
-
return execSync('git rev-parse --abbrev-ref HEAD').toString().trim();
|
|
10
|
-
}
|
|
11
|
-
catch (error) {
|
|
12
|
-
console.error(chalk.red('Error getting current branch:'), error);
|
|
13
|
-
throw new Error('Failed to get current branch');
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
function getDefaultBaseBranch() {
|
|
17
|
-
try {
|
|
18
|
-
// First check for a default branch set in git config
|
|
19
|
-
try {
|
|
20
|
-
const defaultBranch = execSync('git config init.defaultBranch').toString().trim();
|
|
21
|
-
if (defaultBranch) {
|
|
22
|
-
return defaultBranch;
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
catch (error) {
|
|
26
|
-
// Continue if git config doesn't have default branch
|
|
27
|
-
}
|
|
28
|
-
// Next, check if GitHub CLI can tell us the default branch
|
|
29
|
-
try {
|
|
30
|
-
const repoInfo = execSync('gh repo view --json defaultBranchRef --jq .defaultBranchRef.name').toString().trim();
|
|
31
|
-
if (repoInfo) {
|
|
32
|
-
return repoInfo;
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
catch (error) {
|
|
36
|
-
// Continue if gh command fails
|
|
37
|
-
}
|
|
38
|
-
// Try to find default branch in remote branches list
|
|
39
|
-
const branches = execSync('git branch -r').toString().trim().split('\n');
|
|
40
|
-
// Common default branch names, in order of likelihood
|
|
41
|
-
const mainPatterns = [
|
|
42
|
-
/origin\/main$/,
|
|
43
|
-
/origin\/master$/,
|
|
44
|
-
/origin\/develop$/,
|
|
45
|
-
/origin\/dev$/,
|
|
46
|
-
/origin\/trunk$/
|
|
47
|
-
];
|
|
48
|
-
// Try each pattern in order
|
|
49
|
-
for (const pattern of mainPatterns) {
|
|
50
|
-
const defaultBranch = branches.find(b => pattern.test(b.trim()));
|
|
51
|
-
if (defaultBranch) {
|
|
52
|
-
return defaultBranch.trim().replace(/^origin\//, '');
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
// Check for a branch that has 'HEAD -> origin/' in it, indicating the default branch
|
|
56
|
-
const headBranch = branches.find(b => b.includes('HEAD -> origin/'));
|
|
57
|
-
if (headBranch) {
|
|
58
|
-
const match = headBranch.match(/HEAD -> origin\/([^,\s]+)/);
|
|
59
|
-
if (match && match[1]) {
|
|
60
|
-
return match[1];
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
// Fallback to 'main' as most GitHub repos use this now
|
|
64
|
-
console.log(chalk.yellow('Could not determine default branch, using "main"'));
|
|
65
|
-
return 'main';
|
|
66
|
-
}
|
|
67
|
-
catch (error) {
|
|
68
|
-
// If we can't determine, default to main
|
|
69
|
-
console.log(chalk.yellow('Error detecting default branch, using "main"'));
|
|
70
|
-
return 'main';
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
function getCommitsSinceBaseBranch(baseBranch) {
|
|
74
|
-
try {
|
|
75
|
-
// Try first with origin/baseBranch
|
|
76
|
-
try {
|
|
77
|
-
const mergeBase = execSync(`git merge-base HEAD origin/${baseBranch}`).toString().trim();
|
|
78
|
-
const commitMessages = execSync(`git log --pretty=format:"%s" ${mergeBase}..HEAD`).toString().trim();
|
|
79
|
-
if (commitMessages) {
|
|
80
|
-
return commitMessages.split('\n').filter(Boolean);
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
catch (error) {
|
|
84
|
-
// If origin/baseBranch doesn't exist, try with just baseBranch
|
|
85
|
-
console.log(chalk.yellow(`No origin/${baseBranch} found, trying with local ${baseBranch} branch...`));
|
|
86
|
-
}
|
|
87
|
-
// Try with local branch
|
|
88
|
-
try {
|
|
89
|
-
const mergeBase = execSync(`git merge-base HEAD ${baseBranch}`).toString().trim();
|
|
90
|
-
const commitMessages = execSync(`git log --pretty=format:"%s" ${mergeBase}..HEAD`).toString().trim();
|
|
91
|
-
if (commitMessages) {
|
|
92
|
-
return commitMessages.split('\n').filter(Boolean);
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
catch (error) {
|
|
96
|
-
// If that fails too, fallback to simple branch comparison
|
|
97
|
-
console.log(chalk.yellow(`Merge base with ${baseBranch} not found, comparing branches directly...`));
|
|
98
|
-
}
|
|
99
|
-
// Direct branch comparison
|
|
100
|
-
try {
|
|
101
|
-
const commitMessages = execSync(`git log --pretty=format:"%s" ${baseBranch}..HEAD`).toString().trim();
|
|
102
|
-
if (commitMessages) {
|
|
103
|
-
return commitMessages.split('\n').filter(Boolean);
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
catch (error) {
|
|
107
|
-
console.log(chalk.yellow(`Could not compare with ${baseBranch}, using recent commits...`));
|
|
108
|
-
}
|
|
109
|
-
// Last resort: get most recent commits
|
|
110
|
-
const commitMessages = execSync('git log --pretty=format:"%s" -n 10').toString().trim();
|
|
111
|
-
return commitMessages.split('\n').filter(Boolean);
|
|
112
|
-
}
|
|
113
|
-
catch (error) {
|
|
114
|
-
console.error(chalk.yellow('Could not get commits. Using empty list.'));
|
|
115
|
-
return [];
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
function getChangedFiles(baseBranch) {
|
|
119
|
-
// Try several methods to get changed files
|
|
120
|
-
// Method 1: Compare with origin/baseBranch using three dots
|
|
121
|
-
try {
|
|
122
|
-
const changedFiles = execSync(`git diff --name-only origin/${baseBranch}...HEAD`).toString().trim();
|
|
123
|
-
if (changedFiles) {
|
|
124
|
-
return changedFiles.split('\n').filter(Boolean);
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
catch (error) {
|
|
128
|
-
// Continue to next method
|
|
129
|
-
}
|
|
130
|
-
// Method 2: Compare with local baseBranch using three dots
|
|
131
|
-
try {
|
|
132
|
-
const changedFiles = execSync(`git diff --name-only ${baseBranch}...HEAD`).toString().trim();
|
|
133
|
-
if (changedFiles) {
|
|
134
|
-
return changedFiles.split('\n').filter(Boolean);
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
catch (error) {
|
|
138
|
-
// Continue to next method
|
|
139
|
-
}
|
|
140
|
-
// Method 3: Direct comparison with two dots
|
|
141
|
-
try {
|
|
142
|
-
const changedFiles = execSync(`git diff --name-only ${baseBranch}..HEAD`).toString().trim();
|
|
143
|
-
if (changedFiles) {
|
|
144
|
-
return changedFiles.split('\n').filter(Boolean);
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
catch (error) {
|
|
148
|
-
// Continue to next method
|
|
149
|
-
}
|
|
150
|
-
// Method 4: Get recently modified files
|
|
151
|
-
try {
|
|
152
|
-
console.log(chalk.yellow(`Could not determine changed files relative to ${baseBranch}, using recently modified files...`));
|
|
153
|
-
const changedFiles = execSync('git ls-files --modified --others --exclude-standard').toString().trim();
|
|
154
|
-
if (changedFiles) {
|
|
155
|
-
return changedFiles.split('\n').filter(Boolean);
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
catch (error) {
|
|
159
|
-
// Last resort
|
|
160
|
-
}
|
|
161
|
-
// Method 5: List all files in the repo as a last resort
|
|
162
|
-
try {
|
|
163
|
-
console.log(chalk.yellow('Using all tracked files as fallback...'));
|
|
164
|
-
const allFiles = execSync('git ls-files').toString().trim();
|
|
165
|
-
return allFiles.split('\n').filter(Boolean).slice(0, 50); // Limit to first 50 files
|
|
166
|
-
}
|
|
167
|
-
catch (error) {
|
|
168
|
-
console.error(chalk.red('Could not determine changed files.'));
|
|
169
|
-
return [];
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
async function generatePRDetails(baseBranch, currentBranch) {
|
|
173
|
-
const config = getConfig();
|
|
174
|
-
if (!config) {
|
|
175
|
-
throw new Error('GitPT is not configured. Please run "gitpt setup" first.');
|
|
176
|
-
}
|
|
177
|
-
const { apiKey, model } = config;
|
|
178
|
-
// Get context for PR
|
|
179
|
-
const commitMessages = getCommitsSinceBaseBranch(baseBranch);
|
|
180
|
-
const changedFiles = getChangedFiles(baseBranch);
|
|
181
|
-
// Check if we have any content to work with
|
|
182
|
-
if (commitMessages.length === 0 && changedFiles.length === 0) {
|
|
183
|
-
console.log(chalk.yellow('No commits or changed files detected.'));
|
|
184
|
-
console.log(chalk.yellow('Will attempt to generate PR details using branch name and repository context.'));
|
|
185
|
-
}
|
|
186
|
-
// Get additional context from repository
|
|
187
|
-
let repoName = "";
|
|
188
|
-
let repoDescription = "";
|
|
189
|
-
try {
|
|
190
|
-
// Try to get repo information from GitHub CLI
|
|
191
|
-
const repoInfo = JSON.parse(execSync('gh repo view --json name,description').toString().trim());
|
|
192
|
-
repoName = repoInfo.name || "";
|
|
193
|
-
repoDescription = repoInfo.description || "";
|
|
194
|
-
}
|
|
195
|
-
catch (error) {
|
|
196
|
-
// Continue without this info
|
|
197
|
-
}
|
|
198
|
-
console.log(chalk.blue('Generating PR title and description...'));
|
|
199
|
-
// Build a rich context for the AI
|
|
200
|
-
let contextSections = [
|
|
201
|
-
`Branch: ${currentBranch}`,
|
|
202
|
-
`Base branch: ${baseBranch}`
|
|
203
|
-
];
|
|
204
|
-
// Add repository info if available
|
|
205
|
-
if (repoName) {
|
|
206
|
-
contextSections.push(`Repository: ${repoName}`);
|
|
207
|
-
}
|
|
208
|
-
if (repoDescription) {
|
|
209
|
-
contextSections.push(`Repository description: ${repoDescription}`);
|
|
210
|
-
}
|
|
211
|
-
// Add commit messages if available
|
|
212
|
-
if (commitMessages.length > 0) {
|
|
213
|
-
contextSections.push('Commit messages in this branch:', commitMessages.map(msg => `- ${msg}`).join('\n'));
|
|
214
|
-
}
|
|
215
|
-
else {
|
|
216
|
-
contextSections.push('No commit messages available.');
|
|
217
|
-
// Try to extract intent from branch name if no commits
|
|
218
|
-
if (currentBranch.includes('/')) {
|
|
219
|
-
const branchParts = currentBranch.split('/');
|
|
220
|
-
const branchType = branchParts[0]; // e.g., "feature", "fix", "chore"
|
|
221
|
-
const branchDescription = branchParts.slice(1).join('/').replace(/-/g, ' ');
|
|
222
|
-
contextSections.push('Branch name analysis:', `Type: ${branchType}`, `Description: ${branchDescription}`);
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
// Add changed files if available
|
|
226
|
-
if (changedFiles.length > 0) {
|
|
227
|
-
contextSections.push('Files changed in this branch:', changedFiles.map(file => `- ${file}`).join('\n'));
|
|
228
|
-
}
|
|
229
|
-
else {
|
|
230
|
-
contextSections.push('No file changes detected.');
|
|
231
|
-
}
|
|
232
|
-
// Create the final context
|
|
233
|
-
const context = contextSections.join('\n\n');
|
|
234
|
-
const systemPrompt = `You are a helpful assistant that generates clear, informative GitHub pull request titles and descriptions.
|
|
235
|
-
For the title:
|
|
236
|
-
- Keep it concise (under 80 characters)
|
|
237
|
-
- Start with a verb in present tense (e.g., "Add", "Fix", "Update")
|
|
238
|
-
- Clearly summarize the main purpose of the changes
|
|
239
|
-
|
|
240
|
-
For the description:
|
|
241
|
-
- Start with a brief summary (1-2 sentences) of what the PR accomplishes
|
|
242
|
-
- Include a more detailed explanation of changes if needed
|
|
243
|
-
- List key changes as bullet points if there are multiple components
|
|
244
|
-
- Include any relevant context that reviewers should know
|
|
245
|
-
- End with any testing instructions if applicable
|
|
246
|
-
|
|
247
|
-
Format the description in Markdown with sections.
|
|
248
|
-
Do not include "PR" or "Pull Request" in the title.`;
|
|
249
|
-
const userPrompt = `Generate a pull request title and description for the following changes:
|
|
250
|
-
|
|
251
|
-
${context}
|
|
252
|
-
|
|
253
|
-
Format your response exactly like this example:
|
|
254
|
-
Title: Add user authentication with JWT
|
|
255
|
-
Description:
|
|
256
|
-
## Summary
|
|
257
|
-
This PR adds user authentication using JWT tokens.
|
|
258
|
-
|
|
259
|
-
## Changes
|
|
260
|
-
- Implement login and registration endpoints
|
|
261
|
-
- Add JWT generation and validation
|
|
262
|
-
- Update user model with password hashing
|
|
263
|
-
- Add authorization middleware
|
|
264
|
-
|
|
265
|
-
## How to test
|
|
266
|
-
1. Register a new user with \`/api/register\`
|
|
267
|
-
2. Login with the new user credentials
|
|
268
|
-
3. Use the returned token to access protected endpoints`;
|
|
269
|
-
try {
|
|
270
|
-
const response = await fetch('https://openrouter.ai/api/v1/chat/completions', {
|
|
271
|
-
method: 'POST',
|
|
272
|
-
headers: {
|
|
273
|
-
'Content-Type': 'application/json',
|
|
274
|
-
'Authorization': `Bearer ${apiKey}`,
|
|
275
|
-
'HTTP-Referer': 'https://github.com/bartaxyz/GitPT',
|
|
276
|
-
},
|
|
277
|
-
body: JSON.stringify({
|
|
278
|
-
model: model,
|
|
279
|
-
messages: [
|
|
280
|
-
{ role: 'system', content: systemPrompt },
|
|
281
|
-
{ role: 'user', content: userPrompt }
|
|
282
|
-
],
|
|
283
|
-
max_tokens: 1000,
|
|
284
|
-
}),
|
|
285
|
-
});
|
|
286
|
-
if (!response.ok) {
|
|
287
|
-
const errorText = await response.text();
|
|
288
|
-
throw new Error(`API request failed: ${response.status} ${response.statusText}\n${errorText}`);
|
|
289
|
-
}
|
|
290
|
-
const data = await response.json();
|
|
291
|
-
const result = data.choices[0].message.content.trim();
|
|
292
|
-
// Parse title and description from AI response
|
|
293
|
-
const titleMatch = result.match(/Title:\s*(.+?)(?:\n|$)/);
|
|
294
|
-
const descMatch = result.match(/Description:\s*\n([\s\S]+)$/);
|
|
295
|
-
const title = titleMatch ? titleMatch[1].trim() : '';
|
|
296
|
-
const body = descMatch ? descMatch[1].trim() : result; // Fallback to full response if parsing fails
|
|
297
|
-
return { title, body };
|
|
298
|
-
}
|
|
299
|
-
catch (error) {
|
|
300
|
-
console.error(chalk.red('Error generating PR details:'), error);
|
|
301
|
-
throw new Error('Failed to generate PR details');
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
function checkGitHubCLIAvailability() {
|
|
305
|
-
try {
|
|
306
|
-
execSync('gh --version', { stdio: 'ignore' });
|
|
307
|
-
return true;
|
|
308
|
-
}
|
|
309
|
-
catch (error) {
|
|
310
|
-
return false;
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
function createPullRequest(title, body, baseBranch, draft) {
|
|
314
|
-
const draftFlag = draft ? '--draft' : '';
|
|
315
|
-
try {
|
|
316
|
-
console.log(chalk.blue(`Creating pull request to ${baseBranch}...`));
|
|
317
|
-
// Create a temporary file for the PR body to avoid issues with escaping
|
|
318
|
-
const tempFilePath = `/tmp/gitpt-pr-body-${Date.now()}.md`;
|
|
319
|
-
try {
|
|
320
|
-
// Write the body to a temporary file
|
|
321
|
-
execSync(`cat > "${tempFilePath}" << 'GITPT_EOF'
|
|
322
|
-
${body}
|
|
323
|
-
GITPT_EOF`);
|
|
324
|
-
// Try to get the remote repo URL if available
|
|
325
|
-
let repoUrlArg = '';
|
|
326
|
-
try {
|
|
327
|
-
const repoUrl = execSync('git config --get remote.origin.url').toString().trim();
|
|
328
|
-
if (repoUrl) {
|
|
329
|
-
repoUrlArg = `--repo "${repoUrl}"`;
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
catch (e) {
|
|
333
|
-
// Proceed without repo URL
|
|
334
|
-
}
|
|
335
|
-
// Use the file for the body
|
|
336
|
-
const command = `gh pr create --title "${title.replace(/"/g, '\\"')}" --body-file "${tempFilePath}" --base "${baseBranch}" ${draftFlag} ${repoUrlArg}`;
|
|
337
|
-
// Set a timeout to avoid hanging indefinitely
|
|
338
|
-
console.log(chalk.gray('Running GitHub PR creation command...'));
|
|
339
|
-
console.log(chalk.gray(`Using base branch: ${baseBranch}`));
|
|
340
|
-
// Add debugging output
|
|
341
|
-
console.log(chalk.gray('Executing command with 60s timeout:'));
|
|
342
|
-
// Execute the command with a timeout
|
|
343
|
-
const result = execSync(command, {
|
|
344
|
-
stdio: 'pipe',
|
|
345
|
-
timeout: 60000 // 60-second timeout
|
|
346
|
-
}).toString();
|
|
347
|
-
console.log(result);
|
|
348
|
-
console.log(chalk.green('✓ Pull request created successfully'));
|
|
349
|
-
}
|
|
350
|
-
finally {
|
|
351
|
-
// Clean up temporary file
|
|
352
|
-
try {
|
|
353
|
-
execSync(`rm -f "${tempFilePath}"`);
|
|
354
|
-
}
|
|
355
|
-
catch (e) {
|
|
356
|
-
// Ignore cleanup errors
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
catch (error) {
|
|
361
|
-
if (error instanceof Error && error.message.includes('timeout')) {
|
|
362
|
-
console.error(chalk.red('Error: GitHub CLI command timed out after 60 seconds.'));
|
|
363
|
-
console.log(chalk.yellow('You may need to create the PR manually using:'));
|
|
364
|
-
console.log(chalk.yellow(`gh pr create --title "${title}" --base "${baseBranch}" ${draftFlag}`));
|
|
365
|
-
}
|
|
366
|
-
else {
|
|
367
|
-
console.error(chalk.red('Error creating pull request:'), error);
|
|
368
|
-
}
|
|
369
|
-
throw new Error('Failed to create pull request');
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
export async function prCreateCommand(options = {}) {
|
|
373
|
-
if (!isGitRepository()) {
|
|
374
|
-
console.error(chalk.red('Error: Not a git repository'));
|
|
375
|
-
process.exit(1);
|
|
376
|
-
}
|
|
377
|
-
// Check if GitHub CLI is available
|
|
378
|
-
if (!checkGitHubCLIAvailability()) {
|
|
379
|
-
console.error(chalk.red('Error: GitHub CLI (gh) is not installed or not available in PATH.'));
|
|
380
|
-
console.log(chalk.yellow('Please install GitHub CLI from https://cli.github.com/'));
|
|
381
|
-
process.exit(1);
|
|
382
|
-
}
|
|
383
|
-
// Check if user is authenticated with GitHub CLI
|
|
384
|
-
try {
|
|
385
|
-
const authStatus = execSync('gh auth status -h github.com 2>&1 || true').toString();
|
|
386
|
-
if (authStatus.includes('not logged')) {
|
|
387
|
-
console.error(chalk.red('Error: You are not authenticated with GitHub CLI.'));
|
|
388
|
-
console.log(chalk.yellow('Please run `gh auth login` to authenticate.'));
|
|
389
|
-
process.exit(1);
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
catch (error) {
|
|
393
|
-
console.log(chalk.yellow('Warning: Could not verify GitHub CLI authentication.'));
|
|
394
|
-
console.log(chalk.yellow('If PR creation fails, please run `gh auth login` first.'));
|
|
395
|
-
}
|
|
396
|
-
// Get configuration
|
|
397
|
-
const config = getConfig();
|
|
398
|
-
if (!config) {
|
|
399
|
-
console.error(chalk.red('GitPT is not configured. Please run "gitpt setup" first.'));
|
|
400
|
-
process.exit(1);
|
|
401
|
-
}
|
|
402
|
-
try {
|
|
403
|
-
const currentBranch = getCurrentBranch();
|
|
404
|
-
const baseBranch = options.base || getDefaultBaseBranch();
|
|
405
|
-
let title = options.title || '';
|
|
406
|
-
let body = options.body || '';
|
|
407
|
-
// Generate PR details if not provided
|
|
408
|
-
if (!title || !body) {
|
|
409
|
-
const generatedDetails = await generatePRDetails(baseBranch, currentBranch);
|
|
410
|
-
title = title || generatedDetails.title;
|
|
411
|
-
body = body || generatedDetails.body;
|
|
412
|
-
console.log(chalk.green('✓ PR details generated'));
|
|
413
|
-
console.log('');
|
|
414
|
-
console.log(chalk.cyan('Generated title:'));
|
|
415
|
-
console.log(title);
|
|
416
|
-
console.log('');
|
|
417
|
-
console.log(chalk.cyan('Generated description:'));
|
|
418
|
-
console.log(body);
|
|
419
|
-
console.log('');
|
|
420
|
-
}
|
|
421
|
-
// Allow editing PR details
|
|
422
|
-
if (options.edit !== false) {
|
|
423
|
-
const answers = await inquirer.prompt([
|
|
424
|
-
{
|
|
425
|
-
type: 'input',
|
|
426
|
-
name: 'title',
|
|
427
|
-
message: 'Edit PR title:',
|
|
428
|
-
default: title
|
|
429
|
-
},
|
|
430
|
-
{
|
|
431
|
-
type: 'editor',
|
|
432
|
-
name: 'body',
|
|
433
|
-
message: 'Edit PR description:',
|
|
434
|
-
default: body
|
|
435
|
-
},
|
|
436
|
-
{
|
|
437
|
-
type: 'confirm',
|
|
438
|
-
name: 'draft',
|
|
439
|
-
message: 'Create as draft PR?',
|
|
440
|
-
default: options.draft || false
|
|
441
|
-
}
|
|
442
|
-
]);
|
|
443
|
-
title = answers.title;
|
|
444
|
-
body = answers.body;
|
|
445
|
-
const isDraft = answers.draft;
|
|
446
|
-
// Create the PR
|
|
447
|
-
createPullRequest(title, body, baseBranch, isDraft);
|
|
448
|
-
}
|
|
449
|
-
else {
|
|
450
|
-
// Create the PR without editing
|
|
451
|
-
createPullRequest(title, body, baseBranch, options.draft || false);
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
catch (error) {
|
|
455
|
-
console.error(chalk.red('Error:'), error instanceof Error ? error.message : String(error));
|
|
456
|
-
process.exit(1);
|
|
457
|
-
}
|
|
458
|
-
}
|
package/dist/utils/api.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function generateCommitMessage(diff: string, validationErrors?: string): Promise<string>;
|
package/dist/utils/api.js
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import fetch from "node-fetch";
|
|
2
|
-
import { getConfig } from "./config.js";
|
|
3
|
-
import { hasCommitlintConfig, getCommitlintRules } from "./commitlint.js";
|
|
4
|
-
const OPENROUTER_API_URL = "https://openrouter.ai/api/v1/chat/completions";
|
|
5
|
-
export async function generateCommitMessage(diff, validationErrors) {
|
|
6
|
-
// Check if commitlint is configured
|
|
7
|
-
const hasCommitlint = hasCommitlintConfig();
|
|
8
|
-
const config = getConfig();
|
|
9
|
-
if (!config) {
|
|
10
|
-
throw new Error('GitPT is not configured. Please run "gitpt setup" first.');
|
|
11
|
-
}
|
|
12
|
-
const { apiKey, model } = config;
|
|
13
|
-
const messages = [
|
|
14
|
-
{
|
|
15
|
-
role: "system",
|
|
16
|
-
content: `You are a helpful assistant that generates concise, informative Git commit messages.
|
|
17
|
-
Follow these strict rules:
|
|
18
|
-
${hasCommitlint ? getCommitlintRules() : `1. Use conventional commit format: type: description
|
|
19
|
-
2. Types are: feat, fix, docs, style, refactor, test, chore
|
|
20
|
-
3. NO scopes in parentheses - do not use feat(scope)
|
|
21
|
-
4. Keep the entire message under 100 characters
|
|
22
|
-
5. Use present tense (e.g., "add feature" not "added feature")
|
|
23
|
-
6. Be brief but descriptive about WHAT changed
|
|
24
|
-
7. Do not include detailed explanations
|
|
25
|
-
8. Examples:
|
|
26
|
-
- feat: add user authentication
|
|
27
|
-
- fix: resolve null pointer in login
|
|
28
|
-
- chore: update dependencies
|
|
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.` : ''}`,
|
|
30
|
-
},
|
|
31
|
-
{
|
|
32
|
-
role: "user",
|
|
33
|
-
content: `Generate a commit message for the following git diff:\n\n${diff}`,
|
|
34
|
-
},
|
|
35
|
-
];
|
|
36
|
-
try {
|
|
37
|
-
const response = await fetch(OPENROUTER_API_URL, {
|
|
38
|
-
method: "POST",
|
|
39
|
-
headers: {
|
|
40
|
-
"Content-Type": "application/json",
|
|
41
|
-
Authorization: `Bearer ${apiKey}`,
|
|
42
|
-
"HTTP-Referer": "https://github.com/bartaxyz/GitPT",
|
|
43
|
-
},
|
|
44
|
-
body: JSON.stringify({
|
|
45
|
-
model: model,
|
|
46
|
-
messages: messages,
|
|
47
|
-
max_tokens: 300,
|
|
48
|
-
}),
|
|
49
|
-
});
|
|
50
|
-
if (!response.ok) {
|
|
51
|
-
const errorText = await response.text();
|
|
52
|
-
throw new Error(`API request failed: ${response.status} ${response.statusText}\n${errorText}`);
|
|
53
|
-
}
|
|
54
|
-
const data = (await response.json());
|
|
55
|
-
return data.choices[0].message.content.trim();
|
|
56
|
-
}
|
|
57
|
-
catch (error) {
|
|
58
|
-
console.error("Error generating commit message:", error);
|
|
59
|
-
throw new Error("Failed to generate commit message");
|
|
60
|
-
}
|
|
61
|
-
}
|
package/dist/utils/config.d.ts
DELETED
package/dist/utils/config.js
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
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
|
-
}
|
package/dist/utils/git.d.ts
DELETED
|
@@ -1,6 +0,0 @@
|
|
|
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;
|
package/dist/utils/git.js
DELETED
|
@@ -1,62 +0,0 @@
|
|
|
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
|
-
}
|