Compare commits

..

10 Commits

Author SHA1 Message Date
di-sukharev
ce47c1fe81 2.2.1 2023-05-26 12:37:20 +08:00
di-sukharev
a880dd6bd2 build 2023-05-26 12:37:16 +08:00
di-sukharev
f193bb1d96 chore(package.json): update version from 2.1.4 to 2.2.0 to reflect new features and improvements 2023-05-26 12:35:11 +08:00
di-sukharev
4cc34dfa5f chore(package.json): update version from 2.1.3 to 2.1.4 to reflect recent changes and improvements 2023-05-26 12:33:18 +08:00
di-sukharev
d034238505 chore(package.json): update version from 2.1.2 to 2.1.3 to reflect recent changes and improvements 2023-05-26 12:27:48 +08:00
di-sukharev
25acb1c219 chore(package.json): update version from 2.1.1 to 2.1.2 to reflect recent changes and improvements 2023-05-26 12:27:28 +08:00
di-sukharev
44c9d48eae 2.1.1 2023-05-26 12:22:15 +08:00
di-sukharev
702eab9ec0 build 2023-05-26 12:22:11 +08:00
di-sukharev
e8be3858ab update v to 2.1.0 2023-05-26 12:22:07 +08:00
di-sukharev
50e7cd3576 chore(package.json): update package version from 2.0.19 to 2.1.0
refactor(cli.ts): uncomment checkIsLatestVersion function call to ensure the latest version of the package is being used
2023-05-26 12:16:56 +08:00
7 changed files with 137 additions and 173 deletions

View File

@@ -16272,7 +16272,7 @@ function G3(t, e2) {
// package.json
var package_default = {
name: "opencommit",
version: "2.0.19",
version: "2.2.0",
description: "Auto-generate impressive commits in 1 second. Killing lame commits with AI \u{1F92F}\u{1F52B}",
keywords: [
"git",
@@ -16313,7 +16313,7 @@ var package_default = {
start: "node ./out/cli.cjs",
dev: "ts-node ./src/cli.ts",
build: "rimraf out && node esbuild.config.js",
deploy: "npm run build && npm version patch && npm publish --tag latest",
deploy: "npm run build:push && npm version patch && git push origin --tags && npm publish --tag latest",
"build:push": "npm run build && git add . && git commit -m 'build' && git push",
lint: "eslint src --ext ts && tsc --noEmit",
format: "prettier --write src"
@@ -21732,6 +21732,17 @@ var OpenAi = class {
}
};
};
var getOpenCommitLatestVersion = async () => {
try {
const { data } = await axios_default.get(
"https://unpkg.com/opencommit/package.json"
);
return data.version;
} catch (_6) {
ce("Error while getting the latest version of opencommit");
return void 0;
}
};
var api = new OpenAi();
// src/utils/tokenCount.ts
@@ -22094,6 +22105,25 @@ ${stagedFiles.map((file) => ` ${file}`).join("\n")}`
process.exit(0);
}
// src/utils/checkIsLatestVersion.ts
var checkIsLatestVersion = async () => {
const latestVersion = await getOpenCommitLatestVersion();
if (latestVersion) {
const currentVersion = package_default.version;
if (currentVersion !== latestVersion) {
console.warn(
source_default.yellow(
`
You are not using the latest stable version of OpenCommit with new features and bug fixes.
Current version: ${currentVersion}. Latest version: ${latestVersion}.
\u{1F680} To update run: npm i -g opencommit@latest.
`
)
);
}
}
};
// src/cli.ts
var extraArgs = process.argv.slice(2);
Z2(
@@ -22106,6 +22136,7 @@ Z2(
help: { description: package_default.description }
},
async () => {
await checkIsLatestVersion();
if (await isHookCalled()) {
prepareCommitMessageHook();
} else {

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "opencommit",
"version": "2.0.19",
"version": "2.2.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "opencommit",
"version": "2.0.19",
"version": "2.2.1",
"license": "MIT",
"dependencies": {
"@actions/core": "^1.10.0",

View File

@@ -1,6 +1,6 @@
{
"name": "opencommit",
"version": "2.0.19",
"version": "2.2.1",
"description": "Auto-generate impressive commits in 1 second. Killing lame commits with AI 🤯🔫",
"keywords": [
"git",
@@ -41,7 +41,7 @@
"start": "node ./out/cli.cjs",
"dev": "ts-node ./src/cli.ts",
"build": "rimraf out && node esbuild.config.js",
"deploy": "npm run build && npm version patch && npm publish --tag latest",
"deploy": "npm run build:push && npm version patch && git push origin --tags && npm publish --tag latest",
"build:push": "npm run build && git add . && git commit -m 'build' && git push",
"lint": "eslint src --ext ts && tsc --noEmit",
"format": "prettier --write src"

View File

@@ -7,9 +7,7 @@ import {
OpenAIApi
} from 'openai';
import {CONFIG_MODES, DEFAULT_MODEL_TOKEN_LIMIT, getConfig} from './commands/config';
import {tokenCount} from './utils/tokenCount';
import {GenerateCommitMessageErrorEnum} from './generateCommitMessageFromGitDiff';
import { CONFIG_MODES, getConfig } from './commands/config';
const config = getConfig();
@@ -58,14 +56,6 @@ class OpenAi {
max_tokens: maxTokens || 500
};
try {
const REQUEST_TOKENS = messages.map(
(msg) => tokenCount(msg.content) + 4
).reduce((a, b) => a + b, 0);
if (REQUEST_TOKENS > (DEFAULT_MODEL_TOKEN_LIMIT - maxTokens)) {
throw new Error(GenerateCommitMessageErrorEnum.tooMuchTokens);
}
const { data } = await this.openAI.createChatCompletion(params);
const message = data.choices[0].message;

View File

@@ -7,7 +7,7 @@ import { configCommand } from './commands/config';
import { hookCommand, isHookCalled } from './commands/githook.js';
import { prepareCommitMessageHook } from './commands/prepare-commit-msg-hook';
import { commit } from './commands/commit';
// import { checkIsLatestVersion } from './utils/checkIsLatestVersion';
import { checkIsLatestVersion } from './utils/checkIsLatestVersion';
const extraArgs = process.argv.slice(2);
@@ -21,7 +21,7 @@ cli(
help: { description: packageJSON.description }
},
async () => {
// await checkIsLatestVersion();
await checkIsLatestVersion();
if (await isHookCalled()) {
prepareCommitMessageHook();

View File

@@ -22,8 +22,6 @@ export enum CONFIG_KEYS {
OCO_LANGUAGE = 'OCO_LANGUAGE'
}
export const DEFAULT_MODEL_TOKEN_LIMIT = 4096;
export enum CONFIG_MODES {
get = 'get',
set = 'set'

View File

@@ -1,28 +1,28 @@
import {
ChatCompletionRequestMessage,
ChatCompletionRequestMessageRoleEnum
ChatCompletionRequestMessage,
ChatCompletionRequestMessageRoleEnum
} from 'openai';
import {api} from './api';
import {DEFAULT_MODEL_TOKEN_LIMIT, getConfig} from './commands/config';
import {mergeDiffs} from './utils/mergeDiffs';
import {i18n, I18nLocals} from './i18n';
import {tokenCount} from './utils/tokenCount';
import { api } from './api';
import { getConfig } from './commands/config';
import { mergeDiffs } from './utils/mergeDiffs';
import { i18n, I18nLocals } from './i18n';
import { tokenCount } from './utils/tokenCount';
const config = getConfig();
const translation = i18n[(config?.OCO_LANGUAGE as I18nLocals) || 'en'];
const INIT_MESSAGES_PROMPT: Array<ChatCompletionRequestMessage> = [
{
role: ChatCompletionRequestMessageRoleEnum.System,
// prettier-ignore
content: `You are to act as the author of a commit message in git. Your mission is to create clean and comprehensive commit messages in the conventional commit convention and explain WHAT were the changes and WHY the changes were done. I'll send you an output of 'git diff --staged' command, and you convert it into a commit message.
${config?.OCO_EMOJI ? 'Use GitMoji convention to preface the commit.' : 'Do not preface the commit with anything.'}
${config?.OCO_DESCRIPTION ? 'Add a short description of WHY the changes are done after the commit message. Don\'t start it with "This commit", just describe the changes.' : "Don't add any descriptions to the commit, only commit message."}
{
role: ChatCompletionRequestMessageRoleEnum.System,
// prettier-ignore
content: `You are to act as the author of a commit message in git. Your mission is to create clean and comprehensive commit messages in the conventional commit convention and explain WHAT were the changes and WHY the changes were done. I'll send you an output of 'git diff --staged' command, and you convert it into a commit message.
${config?.OCO_EMOJI ? 'Use GitMoji convention to preface the commit.': 'Do not preface the commit with anything.'}
${config?.OCO_DESCRIPTION ? 'Add a short description of WHY the changes are done after the commit message. Don\'t start it with "This commit", just describe the changes.': "Don't add any descriptions to the commit, only commit message."}
Use the present tense. Lines must not be longer than 74 characters. Use ${translation.localLanguage} to answer.`
},
{
role: ChatCompletionRequestMessageRoleEnum.User,
content: `diff --git a/src/server.ts b/src/server.ts
},
{
role: ChatCompletionRequestMessageRoleEnum.User,
content: `diff --git a/src/server.ts b/src/server.ts
index ad4db42..f3b18a9 100644
--- a/src/server.ts
+++ b/src/server.ts
@@ -46,183 +46,128 @@ app.use((_, res, next) => {
+app.listen(process.env.PORT || PORT, () => {
+ console.log(\`Server listening on port \${PORT}\`);
});`
},
{
role: ChatCompletionRequestMessageRoleEnum.Assistant,
content: `${config?.OCO_EMOJI ? '🐛 ' : ''}${translation.commitFix}
},
{
role: ChatCompletionRequestMessageRoleEnum.Assistant,
content: `${config?.OCO_EMOJI ? '🐛 ' : ''}${translation.commitFix}
${config?.OCO_EMOJI ? '✨ ' : ''}${translation.commitFeat}
${config?.OCO_DESCRIPTION ? translation.commitDescription : ''}`
}
}
];
const generateCommitMessageChatCompletionPrompt = (
diff: string
diff: string
): Array<ChatCompletionRequestMessage> => {
const chatContextAsCompletionRequest = [...INIT_MESSAGES_PROMPT];
const chatContextAsCompletionRequest = [...INIT_MESSAGES_PROMPT];
chatContextAsCompletionRequest.push({
role: ChatCompletionRequestMessageRoleEnum.User,
content: diff
});
chatContextAsCompletionRequest.push({
role: ChatCompletionRequestMessageRoleEnum.User,
content: diff
});
return chatContextAsCompletionRequest;
return chatContextAsCompletionRequest;
};
export enum GenerateCommitMessageErrorEnum {
tooMuchTokens = 'TOO_MUCH_TOKENS',
internalError = 'INTERNAL_ERROR',
emptyMessage = 'EMPTY_MESSAGE'
tooMuchTokens = 'TOO_MUCH_TOKENS',
internalError = 'INTERNAL_ERROR',
emptyMessage = 'EMPTY_MESSAGE'
}
const INIT_MESSAGES_PROMPT_LENGTH = INIT_MESSAGES_PROMPT.map(
(msg) => tokenCount(msg.content) + 4
(msg) => tokenCount(msg.content) + 4
).reduce((a, b) => a + b, 0);
const ADJUSTMENT_FACTOR = 20;
const MAX_REQ_TOKENS = 3000 - INIT_MESSAGES_PROMPT_LENGTH;
export const generateCommitMessageByDiff = async (
diff: string
diff: string
): Promise<string> => {
try {
const MAX_REQUEST_TOKENS = DEFAULT_MODEL_TOKEN_LIMIT
- ADJUSTMENT_FACTOR
- INIT_MESSAGES_PROMPT_LENGTH
- config?.OCO_OPENAI_MAX_TOKENS;
try {
if (tokenCount(diff) >= MAX_REQ_TOKENS) {
const commitMessagePromises = getCommitMsgsPromisesFromFileDiffs(
diff,
MAX_REQ_TOKENS
);
if (tokenCount(diff) >= MAX_REQUEST_TOKENS) {
const commitMessagePromises = getCommitMsgsPromisesFromFileDiffs(
diff,
MAX_REQUEST_TOKENS
);
const commitMessages = await Promise.all(commitMessagePromises);
const commitMessages = [];
for (const promise of commitMessagePromises) {
commitMessages.push(await promise);
await delay(2000);
}
return commitMessages.join('\n\n');
} else {
const messages = generateCommitMessageChatCompletionPrompt(diff);
return commitMessages.join('\n\n');
} else {
const messages = generateCommitMessageChatCompletionPrompt(diff);
const commitMessage = await api.generateCommitMessage(messages);
const commitMessage = await api.generateCommitMessage(messages);
if (!commitMessage)
throw new Error(GenerateCommitMessageErrorEnum.emptyMessage);
if (!commitMessage)
throw new Error(GenerateCommitMessageErrorEnum.emptyMessage);
return commitMessage;
}
} catch (error) {
throw error;
return commitMessage;
}
} catch (error) {
throw error;
}
};
function getMessagesPromisesByChangesInFile(
fileDiff: string,
separator: string,
maxChangeLength: number
fileDiff: string,
separator: string,
maxChangeLength: number
) {
const hunkHeaderSeparator = '@@ ';
const [fileHeader, ...fileDiffByLines] = fileDiff.split(hunkHeaderSeparator);
const hunkHeaderSeparator = '@@ ';
const [fileHeader, ...fileDiffByLines] = fileDiff.split(hunkHeaderSeparator);
// merge multiple line-diffs into 1 to save tokens
const mergedChanges = mergeDiffs(
fileDiffByLines.map((line) => hunkHeaderSeparator + line),
maxChangeLength
// merge multiple line-diffs into 1 to save tokens
const mergedChanges = mergeDiffs(
fileDiffByLines.map((line) => hunkHeaderSeparator + line),
maxChangeLength
);
const lineDiffsWithHeader = mergedChanges.map(
(change) => fileHeader + change
);
const commitMsgsFromFileLineDiffs = lineDiffsWithHeader.map((lineDiff) => {
const messages = generateCommitMessageChatCompletionPrompt(
separator + lineDiff
);
const lineDiffsWithHeader = [];
for (const change of mergedChanges) {
const totalChange = fileHeader + change;
if (tokenCount(totalChange) > maxChangeLength) {
// If the totalChange is too large, split it into smaller pieces
const splitChanges = splitDiff(totalChange, maxChangeLength);
lineDiffsWithHeader.push(...splitChanges);
} else {
lineDiffsWithHeader.push(totalChange);
}
}
return api.generateCommitMessage(messages);
});
const commitMsgsFromFileLineDiffs = lineDiffsWithHeader.map((lineDiff) => {
const messages = generateCommitMessageChatCompletionPrompt(
separator + lineDiff
);
return api.generateCommitMessage(messages);
});
return commitMsgsFromFileLineDiffs;
}
function splitDiff(diff: string, maxChangeLength: number) {
const lines = diff.split('\n');
const splitDiffs = [];
let currentDiff = '';
for (let line of lines) {
// If a single line exceeds maxChangeLength, split it into multiple lines
while (tokenCount(line) > maxChangeLength) {
const subLine = line.substring(0, maxChangeLength);
line = line.substring(maxChangeLength);
splitDiffs.push(subLine);
}
// Check the tokenCount of the currentDiff and the line separately
if (tokenCount(currentDiff) + tokenCount('\n' + line) > maxChangeLength) {
// If adding the next line would exceed the maxChangeLength, start a new diff
splitDiffs.push(currentDiff);
currentDiff = line;
} else {
// Otherwise, add the line to the current diff
currentDiff += '\n' + line;
}
}
// Add the last diff
if (currentDiff) {
splitDiffs.push(currentDiff);
}
return splitDiffs;
return commitMsgsFromFileLineDiffs;
}
export function getCommitMsgsPromisesFromFileDiffs(
diff: string,
maxDiffLength: number
diff: string,
maxDiffLength: number
) {
const separator = 'diff --git ';
const separator = 'diff --git ';
const diffByFiles = diff.split(separator).slice(1);
const diffByFiles = diff.split(separator).slice(1);
// merge multiple files-diffs into 1 prompt to save tokens
const mergedFilesDiffs = mergeDiffs(diffByFiles, maxDiffLength);
// merge multiple files-diffs into 1 prompt to save tokens
const mergedFilesDiffs = mergeDiffs(diffByFiles, maxDiffLength);
const commitMessagePromises = [];
const commitMessagePromises = [];
for (const fileDiff of mergedFilesDiffs) {
if (tokenCount(fileDiff) >= maxDiffLength) {
// if file-diff is bigger than gpt context — split fileDiff into lineDiff
const messagesPromises = getMessagesPromisesByChangesInFile(
fileDiff,
separator,
maxDiffLength
);
for (const fileDiff of mergedFilesDiffs) {
if (tokenCount(fileDiff) >= maxDiffLength) {
// if file-diff is bigger than gpt context — split fileDiff into lineDiff
const messagesPromises = getMessagesPromisesByChangesInFile(
fileDiff,
separator,
maxDiffLength
);
commitMessagePromises.push(...messagesPromises);
} else {
const messages = generateCommitMessageChatCompletionPrompt(
separator + fileDiff
);
commitMessagePromises.push(...messagesPromises);
} else {
const messages = generateCommitMessageChatCompletionPrompt(
separator + fileDiff
);
commitMessagePromises.push(api.generateCommitMessage(messages));
}
commitMessagePromises.push(api.generateCommitMessage(messages));
}
return commitMessagePromises;
}
function delay(ms: number) {
return new Promise(resolve => setTimeout(resolve, ms));
}
return commitMessagePromises;
}