Compare commits

..

26 Commits

Author SHA1 Message Date
di-sukharev
1347962a93 1.0.10 2023-03-10 22:42:33 +08:00
di-sukharev
3ca9b9752f * fix(api.ts): add process.exit(1) to terminate the process if apiKey is not provided 2023-03-10 22:41:53 +08:00
di-sukharev
50e2f67a9b 1.0.9 2023-03-10 17:56:21 +08:00
di-sukharev
c1a4c3daf2 * chore: update package description and keywords
* chore: update README with new package description and title
2023-03-10 17:55:51 +08:00
di-sukharev
e4de6d8186 * feat(README.md): add OpenCommit example image to README
* chore(README.md): update OPENAI_API_KEY config key to use 'your_api_key' instead of 'your token'
* chore(README.md): update description of where the api key is stored
2023-03-10 15:59:57 +08:00
di-sukharev
5abe5d715d * refactor(commit.ts): remove unnecessary blank lines
* feat(commit.ts): add spinner to show progress of `git push` command
* feat(commit.ts): show success message after successful `git push` command execution
2023-03-10 15:24:49 +08:00
di-sukharev
32c34abd22 1.0.8 2023-03-09 21:32:24 +08:00
di-sukharev
1050cad95d * refactor(commit.ts): extract stdout from git push command into a variable 2023-03-09 21:32:07 +08:00
di-sukharev
7bbc97980e * refactor(commit.ts): remove unused stdout variable and simplify pushSpinner.stop() call 2023-03-09 21:31:31 +08:00
di-sukharev
2a9a3d5818 * chore(README.md): add emoji to the project description 2023-03-09 21:30:44 +08:00
di-sukharev
22935f38ba 1.0.7 2023-03-09 21:29:52 +08:00
di-sukharev
4ae6361ad8 * chore(package.json): update description with emojis. 2023-03-09 21:29:28 +08:00
di-sukharev
5a0a384cbe 1.0.6 2023-03-09 21:25:49 +08:00
di-sukharev
a8839353f7 * chore(commit.ts): add spinner to indicate git push process
* refactor(commit.ts): change success message to include spinner in `git push` process
2023-03-09 21:25:38 +08:00
di-sukharev
88006b8693 * fix(commit.ts): add check for cancelation of git push confirmation prompt 2023-03-09 21:23:58 +08:00
di-sukharev
5d9d1c972a 1.0.5 2023-03-09 21:22:46 +08:00
di-sukharev
3892bd0e69 * feat(commit.ts): add option to push commits after successful commit 2023-03-09 21:22:35 +08:00
di-sukharev
2b6cc5c360 * chore(README.md): update example section to clarify that all commits in the repo are done with OpenCommit 2023-03-09 21:20:17 +08:00
di-sukharev
c4c2600cf6 1.0.4 2023-03-09 15:29:34 +08:00
di-sukharev
c361aaa914 * refactor(generateCommitMessageFromGitDiff.ts): merge multiple line-diffs and multiple files-diffs to save tokens
* refactor(generateCommitMessageFromGitDiff.ts): split file-diff into line-diffs only if file-diff is bigger than gpt context
2023-03-09 15:28:18 +08:00
di-sukharev
f3d673185e deleted log txt file 2023-03-09 15:04:51 +08:00
di-sukharev
5615bdce86 * refactor(commit.ts): add stdout to commit function output
* fix(commit.ts): fix commit confirmation message to show correct status
2023-03-09 14:02:04 +08:00
di-sukharev
68b327bee7 * refactor(prepare-commit-msg-hook.ts): remove outro function call and its message from try block 2023-03-09 14:00:50 +08:00
di-sukharev
b747d70e69 1.0.3 2023-03-09 13:59:55 +08:00
di-sukharev
caa64fbcf9 * refactor(prepare-commit-msg-hook.ts): change appendFile to writeFile to write commit message to file
* chore(prepare-commit-msg-hook.ts): add newline character before appending file content to commit message
2023-03-09 13:58:27 +08:00
di-sukharev
1a49c08409 * chore(commit.ts): change stagedFilesSpinner message when no files are staged 2023-03-09 13:56:03 +08:00
9 changed files with 53 additions and 143 deletions

BIN
.github/opencommit-example.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 318 KiB

View File

@@ -5,15 +5,18 @@
<h4 align="center">Author <a href="https://github.com/di-sukharev">@di-sukharev</a> <a href="https://twitter.com/io_Y_oi"><img src="https://img.shields.io/twitter/follow/io_Y_oi?style=flat&label=io_Y_oi&logo=twitter&color=0bf&logoColor=fff" align="center"></a>
</h4>
</div>
<p>AI generates conventional commits with mind-blowing accuracy</p>
<h2>GPT CLI to auto-generate impressive commits in 1 second</h2>
<p>Killing lame commits with AI 🤯🔫</p>
<a href="https://www.npmjs.com/package/opencommit"><img src="https://img.shields.io/npm/v/opencommit" alt="Current version"></a>
</div>
---
## Examples
<div align="center">
<img src=".github/opencommit-example.png" alt="OpenCommit example"/>
</div>
Look into [the commits](https://github.com/di-sukharev/opencommit/commit/eae7618d575ee8d2e9fff5de56da79d40c4bc5fc) to see how OpenCommit works. Emoji and long commit description text is configurable.
All the commits in this repo are done with OpenCommit — look into [the commits](https://github.com/di-sukharev/opencommit/commit/eae7618d575ee8d2e9fff5de56da79d40c4bc5fc) to see how OpenCommit works. Emoji and long commit description text is configurable.
## Setup
@@ -30,10 +33,10 @@ Look into [the commits](https://github.com/di-sukharev/opencommit/commit/eae7618
3. Set the key to opencommit config:
```sh
opencommit config set OPENAI_API_KEY=<your token>
opencommit config set OPENAI_API_KEY=<your_api_key>
```
Your token isn't sent to anyone, it's saved in `~/.opencommit` config file.
Your api key is stored locally in `~/.opencommit` config file.
## Usage

120
di.txt
View File

@@ -1,120 +0,0 @@
diff --git a/src/generateCommitMessageFromGitDiff.ts b/src/generateCommitMessageFromGitDiff.ts
index c3fb638..82b8bde 100644
--- a/src/generateCommitMessageFromGitDiff.ts
+++ b/src/generateCommitMessageFromGitDiff.ts
@@ -4,6 +4,7 @@ import {
} from 'openai';
import { api } from './api';
import { getConfig } from './commands/config';
+import { mergeStrings } from './utils/mergeStrings';
const config = getConfig();
@@ -88,37 +89,64 @@ export const generateCommitMessageWithChatCompletion = async (
): Promise<string | GenerateCommitMessageError> => {
try {
if (diff.length >= MAX_REQ_TOKENS) {
- const separator = 'diff --git ';
-
- const diffByFiles = diff.split(separator).slice(1);
-
- const commitMessagePromises = diffByFiles
- .map((fileDiff) => {
- // TODO: split by files
- if (fileDiff.length >= MAX_REQ_TOKENS) return null;
-
- const messages = generateCommitMessageChatCompletionPrompt(
- separator + fileDiff
- );
-
- return api.generateCommitMessage(messages);
- })
- .filter(Boolean);
+ const commitMessagePromises = getCommitMsgsPromisesFromFileDiffs(diff);
const commitMessages = await Promise.all(commitMessagePromises);
return commitMessages.join('\n\n');
- }
-
- const messages = generateCommitMessageChatCompletionPrompt(diff);
+ } else {
+ const messages = generateCommitMessageChatCompletionPrompt(diff);
- const commitMessage = await api.generateCommitMessage(messages);
+ const commitMessage = await api.generateCommitMessage(messages);
- if (!commitMessage)
- return { error: GenerateCommitMessageErrorEnum.emptyMessage };
+ if (!commitMessage)
+ return { error: GenerateCommitMessageErrorEnum.emptyMessage };
- return commitMessage;
+ return commitMessage;
+ }
} catch (error) {
return { error: GenerateCommitMessageErrorEnum.internalError };
}
};
+
+function getMessagesPromisesByLines(fileDiff: string, separator: string) {
+ const [fileHeader, ...fileDiffByLines] = fileDiff.split('@@');
+ const lineDiffsWithHeader = fileDiffByLines.map((d) => fileHeader + '@@' + d);
+
+ const mergedLines = mergeStrings(lineDiffsWithHeader, MAX_REQ_TOKENS);
+
+ const commitMsgsFromFileLineDiffs = mergedLines.map((d) => {
+ const messages = generateCommitMessageChatCompletionPrompt(separator + d);
+
+ return api.generateCommitMessage(messages);
+ });
+
+ return commitMsgsFromFileLineDiffs;
+}
+
+function getCommitMsgsPromisesFromFileDiffs(diff: string) {
+ const separator = 'diff --git ';
+
+ const diffByFiles = diff.split(separator).slice(1);
+
+ const mergedDiffs = mergeStrings(diffByFiles, MAX_REQ_TOKENS);
+
+ const commitMessagePromises = [];
+
+ for (const fileDiff of mergedDiffs) {
+ if (fileDiff.length >= MAX_REQ_TOKENS) {
+ // split fileDiff into lineDiff
+ const messagesPromises = getMessagesPromisesByLines(fileDiff, separator);
+
+ commitMessagePromises.push(...messagesPromises);
+ } else {
+ // generate commits for files
+ const messages = generateCommitMessageChatCompletionPrompt(
+ separator + fileDiff
+ );
+
+ commitMessagePromises.push(api.generateCommitMessage(messages));
+ }
+ }
+ return commitMessagePromises;
+}
diff --git a/src/utils/mergeStrings.ts b/src/utils/mergeStrings.ts
new file mode 100644
index 0000000..a8beb37
--- /dev/null
+++ b/src/utils/mergeStrings.ts
@@ -0,0 +1,14 @@
+export function mergeStrings(arr: string[], maxStringLength: number): string[] {
+ const mergedArr: string[] = [];
+ let currentItem: string = arr[0];
+ for (const item of arr.slice(1)) {
+ if (currentItem.length + item.length <= maxStringLength) {
+ currentItem += item;
+ } else {
+ mergedArr.push(currentItem);
+ currentItem = item;
+ }
+ }
+ mergedArr.push(currentItem);
+ return mergedArr;
+}

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "open-commit",
"version": "1.0.2",
"version": "1.0.10",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "open-commit",
"version": "1.0.2",
"version": "1.0.10",
"license": "ISC",
"dependencies": {
"@clack/prompts": "^0.6.1",

View File

@@ -1,13 +1,17 @@
{
"name": "opencommit",
"version": "1.0.2",
"description": "AI generates conventional commits with mind-blowing accuracy.",
"version": "1.0.10",
"description": "GPT CLI to auto-generate impressive commits in 1 second. Killing lame commits with AI 🤯🔫",
"keywords": [
"git",
"chatgpt",
"gpt",
"ai",
"openai",
"opencommit",
"aicommit",
"aicommits",
"gptcommit",
"commit"
],
"main": "cli.js",

View File

@@ -20,6 +20,7 @@ if (!apiKey) {
outro(
'For help Look into README https://github.com/di-sukharev/opencommit#setup'
);
process.exit(1);
}
// if (!apiKey) {

View File

@@ -44,8 +44,22 @@ ${chalk.grey('——————————————————')}`
});
if (isCommitConfirmedByUser && !isCancel(isCommitConfirmedByUser)) {
await execa('git', ['commit', '-m', commitMessage]);
const { stdout } = await execa('git', ['commit', '-m', commitMessage]);
outro(`${chalk.green('✔')} successfully committed`);
outro(stdout);
const isPushConfirmedByUser = await confirm({
message: 'Do you want to run `git push`?'
});
if (isPushConfirmedByUser && !isCancel(isPushConfirmedByUser)) {
const pushSpinner = spinner();
pushSpinner.start('Running `git push`');
const { stdout } = await execa('git', ['push']);
pushSpinner.stop(`${chalk.green('✔')} successfully pushed all commits`);
if (stdout) outro(stdout);
}
} else outro(`${chalk.gray('✖')} process cancelled`);
};
@@ -79,7 +93,7 @@ export async function commit(isStageAllFlag = false) {
.bold('`oc`')} command.`
);
stagedFilesSpinner.stop('Counting staged files');
stagedFilesSpinner.stop('No files are staged');
const isStageAllAndCommitConfirmedByUser = await confirm({
message: 'Do you want to stage all files and generate commit message?'
});

View File

@@ -37,10 +37,12 @@ export const prepareCommitMessageHook = async () => {
if (typeof commitMessage !== 'string') throw new Error(commitMessage.error);
// TODO: change to read file > write file with commitMessage
await fs.appendFile(messageFilePath, commitMessage);
const fileContent = await fs.readFile(messageFilePath);
outro(`${chalk.green('✔')} commit done`);
await fs.writeFile(
messageFilePath,
commitMessage + '\n' + fileContent.toString()
);
} catch (error) {
outro(`${chalk.red('✖')} ${error}`);
process.exit(1);

View File

@@ -110,14 +110,20 @@ export const generateCommitMessageWithChatCompletion = async (
};
function getMessagesPromisesByLines(fileDiff: string, separator: string) {
const [fileHeader, ...fileDiffByLines] = fileDiff.split('\n@@');
const lineDiffsWithHeader = fileDiffByLines.map(
(d) => fileHeader + '\n@@' + d
const lineSeparator = '\n@@';
const [fileHeader, ...fileDiffByLines] = fileDiff.split(lineSeparator);
// merge multiple line-diffs into 1 to save tokens
const mergedLines = mergeStrings(
fileDiffByLines.map((line) => lineSeparator + line),
MAX_REQ_TOKENS
);
const mergedLines = mergeStrings(lineDiffsWithHeader, MAX_REQ_TOKENS);
const lineDiffsWithHeader = mergedLines.map(
(d) => fileHeader + lineSeparator + d
);
const commitMsgsFromFileLineDiffs = mergedLines.map((d) => {
const commitMsgsFromFileLineDiffs = lineDiffsWithHeader.map((d) => {
const messages = generateCommitMessageChatCompletionPrompt(separator + d);
return api.generateCommitMessage(messages);
@@ -131,18 +137,18 @@ function getCommitMsgsPromisesFromFileDiffs(diff: string) {
const diffByFiles = diff.split(separator).slice(1);
const mergedDiffs = mergeStrings(diffByFiles, MAX_REQ_TOKENS);
// merge multiple files-diffs into 1 prompt to save tokens
const mergedFilesDiffs = mergeStrings(diffByFiles, MAX_REQ_TOKENS);
const commitMessagePromises = [];
for (const fileDiff of mergedDiffs) {
for (const fileDiff of mergedFilesDiffs) {
if (fileDiff.length >= MAX_REQ_TOKENS) {
// split fileDiff into lineDiff
// if file-diff is bigger than gpt context — split fileDiff into lineDiff
const messagesPromises = getMessagesPromisesByLines(fileDiff, separator);
commitMessagePromises.push(...messagesPromises);
} else {
// generate commits for files
const messages = generateCommitMessageChatCompletionPrompt(
separator + fileDiff
);