Compare commits

..

84 Commits

Author SHA1 Message Date
di-sukharev
3fe57537ad 1.1.35 2023-03-30 15:15:00 +08:00
di-sukharev
db9cff1ae1 refactor(githook.ts): add switch statement to determine path separator based on platform
fix(githook.ts): change path separator to use the determined separator variable
2023-03-30 15:14:43 +08:00
di-sukharev
1c29b91408 1.1.34 2023-03-30 15:01:20 +08:00
di-sukharev
425eeef732 1.1.33 2023-03-30 15:01:15 +08:00
di-sukharev
52c396eb16 feat(commit.ts): add support for aborting git push command if user chooses to do so 2023-03-30 15:00:46 +08:00
di-sukharev
f5bcf58f7b refactor(commit.ts): replace filter callback with Boolean function call
docs(commit.ts): remove unnecessary empty line
2023-03-30 14:57:22 +08:00
di-sukharev
4b53a08653 refactor(git.ts): change double quotes to single quotes in git commands and remove unnecessary whitespace 2023-03-30 14:53:18 +08:00
di-sukharev
95d3d8b6c9 chore(commit.ts, es_ES.json, id_ID.json, index.ts): remove unnecessary comments and fix typos in commit messages and translations 2023-03-30 14:52:01 +08:00
kakandavorever
2c79bf22df Add support for indonesia language (id_ID) (#94)
* Add support for indonesia language (id_ID)
2023-03-30 14:47:57 +08:00
Jeroen Smink
e8c1a75a46 Feat(i18n): Add Dutch translations (#85)
* feat(i18n): add support for Dutch language (nl) and translations for commit messages in Dutch

* chore(i18n): update Dutch translation of commit messages in nl.json file

---------

Co-authored-by: Jeroen Smink [TRES] <jeroensmink@TRES.local>
Co-authored-by: Sukharev <57486732+di-sukharev@users.noreply.github.com>
2023-03-30 14:43:12 +08:00
Maik Stegemann
5d064ac873 fix(githook.ts): fix SYMLINK_URL for Windows (#88)
* fix(githook.ts): fix SYMLINK_URL for Windows

* refactor(githook.ts): use path.join to create SYMLINK_URL
The SYMLINK_URL variable is now created using the path.join method, which is more platform-independent and ensures that the correct path separator is used. This improves the reliability of the code and makes it easier to maintain.

---------

Co-authored-by: Maik Stegemann <ms@lis-gmbh.com>
2023-03-30 14:42:02 +08:00
Alex Cáceres Gómez
8d01829a9b 🔥 refactor(githook.ts): remove unused import (#93)
The import statement for the fileURLToPath function from the url module is not used in the file. Therefore, it has been removed to improve code readability and maintainability.
2023-03-30 14:41:08 +08:00
di-sukharev
e9c66ae168 1.1.32 2023-03-29 18:24:18 +08:00
di-sukharev
18b0004b81 1.1.31 2023-03-29 18:24:14 +08:00
di-sukharev
4d4157087e Merge branch 'dev' of github.com:di-sukharev/opencommit into dev 2023-03-29 18:23:46 +08:00
di-sukharev
3edb6e2fc1 chore(generateCommitMessageFromGitDiff.ts): fix typo in GitMoji convention 2023-03-29 18:23:25 +08:00
Andrew
d428102a67 [FEAT](i18n): Add support for Russian language (ru) (#75) 2023-03-29 14:11:18 +08:00
di-sukharev
9404f5d410 1.1.30 2023-03-29 11:31:38 +08:00
di-sukharev
8c1eb4a5ad 1.1.29 2023-03-29 11:31:35 +08:00
di-sukharev
bafe7e9ede refactor(prepare-commit-msg-hook.ts): simplify conditional statements
The conditional statements in the prepareCommitMessageHook function have been simplified to improve readability. The first conditional statement now checks if there are no staged files and no changed files, and if so, it will output a message and exit the process. The second conditional statement now checks if there are no staged files but there are changed files, and if so, it will add the changed files to the staging area.
2023-03-29 11:31:27 +08:00
di-sukharev
a4716b35a4 1.1.28 2023-03-29 11:26:59 +08:00
di-sukharev
c1e9062ce0 1.1.27 2023-03-29 11:26:51 +08:00
di-sukharev
c7efa6f935 refactor: remove @dqbd/tiktoken dependency
chore(tsconfig.json): change target to ESNext
The @dqbd/tiktoken dependency was removed from the package.json file. This dependency was not being used in the project and was therefore removed to reduce the size of the project. The target in the tsconfig.json file was changed from ES2020 to ESNext to allow for the use of the latest ECMAScript features.
2023-03-29 11:26:19 +08:00
di-sukharev
1b70de1d20 fix(githook.ts): capitalize OpenCommit in console output
chore(githook.ts): remove unused import
refactor(tokenCount.ts): simplify token count calculation
The console output now correctly capitalizes OpenCommit for consistency with the project name. The unused import of fileURLToPath is removed. The tokenCount function is refactored to simplify the calculation of the token count. The Tiktoken library is no longer used, and the token count is now calculated based on the length of the content divided by an average token length of 2.7 characters.
2023-03-29 11:26:05 +08:00
di-sukharev
853662acc4 refactor(tokenCount.ts): format code with consistent indentation and quotes
The code has been reformatted to use consistent indentation and quotes. This improves the readability and maintainability of the code. No functionality has been changed.
2023-03-29 11:25:48 +08:00
di-sukharev
0e1ad33179 1.1.26 2023-03-29 10:45:35 +08:00
di-sukharev
e7eaa5425e 1.1.25 2023-03-29 10:45:30 +08:00
di-sukharev
4b96670374 Merge branch 'dev' 2023-03-29 10:45:17 +08:00
di-sukharev
e128cdece1 refactor(generateCommitMessageFromGitDiff.ts): rename mergeStrings to mergeDiffs and add maxDiffLength parameter
feat(generateCommitMessageFromGitDiff.ts): split file diffs into changes in files if they exceed the maximum token count
The mergeStrings function was renamed to mergeDiffs to better reflect its purpose. The function now takes an additional parameter, maxDiffLength, which is used to split file diffs into changes in files if they exceed the maximum token count. This change improves the efficiency of the function and reduces the number of tokens used.
2023-03-29 10:44:45 +08:00
di-sukharev
4cc73208cd chore(.gitignore): add test.ts to the list of ignored files
fix(prepare-commit-msg-hook.ts): add missing await keyword to getStagedFiles() function call
feat(prepare-commit-msg-hook.ts): add spinner to indicate commit message generation progress
feat(utils/mergeDiffs.ts): add mergeDiffs function to merge array of strings into an array of strings with a maximum length
The test.ts file is now ignored by git. The missing await keyword has been added to the getStagedFiles() function call. A spinner has been added to indicate the progress of commit message generation. The mergeDiffs function has been added to merge an array of strings into an array of strings with a maximum length.
2023-03-29 10:43:27 +08:00
di-sukharev
ea864d18f4 refactor(generateCommitMessageFromGitDiff.ts): update INIT_MESSAGES_PROMPT to improve readability and remove unnecessary line breaks 2023-03-29 09:35:35 +08:00
di-sukharev
5d131e66fa Merge branch 'master' into dev 2023-03-29 09:34:38 +08:00
di-sukharev
bf24be92a1 chore(generateCommitMessageFromGitDiff.ts): update INIT_MESSAGES_PROMPT to clarify commit message conventions and instructions 2023-03-29 09:32:15 +08:00
Raymond
3103ae18b8 Count file diff by token, not by length of string (#63)
* 1.1.23

* 1.1.24

* feat(package.json): add @dqbd/tiktoken dependency

refactor(generateCommitMessageFromGitDiff.ts): add tokenCount function to count the number of tokens in a string
refactor(generateCommitMessageFromGitDiff.ts): change the way the length of INIT_MESSAGES_PROMPT is calculated to use tokenCount function
refactor(generateCommitMessageFromGitDiff.ts): change the way the length of diff is calculated to use tokenCount function

refactor(generateCommitMessageFromGitDiff.ts): rename function parameter from diff to fileDiff and update function calls accordingly
feat(generateCommitMessageFromGitDiff.ts): add tokenCount function to count tokens in fileDiff and use it to check if fileDiff is bigger than MAX_REQ_TOKENS

feat(utils): add tokenCount function to count the number of tokens in a string
refactor(utils/mergeStrings.ts): use tokenCount function to count the number of tokens in a string instead of checking the length of the concatenated string

---------

Co-authored-by: di-sukharev <dim.sukharev@gmail.com>
2023-03-28 18:43:02 +08:00
R4V3N
7c9feba3ba Added Swedish Translation (#68)
* 1.1.23

* 1.1.24

* Added Swedish langauge

---------

Co-authored-by: di-sukharev <dim.sukharev@gmail.com>
2023-03-28 18:38:49 +08:00
di-sukharev
58369e4df9 1.1.24 2023-03-28 11:32:17 +08:00
di-sukharev
3d50a67ece 1.1.23 2023-03-28 11:32:13 +08:00
di-sukharev
a2d03e054c 🔀 chore(CommandsEnum.ts): reorder COMMANDS enum values
🐛 fix(config.ts): add missing new lines
🐛 fix(githook.ts): capitalize OpenCommit and fix grammar
The COMMANDS enum values were reordered to improve readability and consistency. In config.ts, missing new lines were added to improve code readability. In githook.ts, OpenCommit was capitalized and grammar was fixed to improve clarity.
2023-03-28 11:31:42 +08:00
di-sukharev
1c113c2901 style(commit.ts): add question mark to confirm message
feat(commit.ts): add support for pushing to selected remote instead of default remote
refactor(commit.ts): extract push command to a function and reuse it for both default and selected remote
2023-03-28 11:22:57 +08:00
di-sukharev
18dcb8e8c2 docs(CONTRIBUTING.md): fix typo in Contacts section
chore(package.json): change license from ISC to MIT
2023-03-28 11:20:09 +08:00
jessicakuijer
8b17b5e906 fix(commit.ts): add --verbose flag to git push command (#43)
* 🐛 fix(commit.ts): add --verbose flag to git push command
The --verbose flag is added to the git push command to provide more detailed output. This helps with debugging and identifying any issues that may arise during the push process.
2023-03-28 11:18:34 +08:00
Dang Quang Linh
284604f6a4 feat(i18n): add support for Vietnamese (Vietnam) language (vi_VN) (#51)
Co-authored-by: Sukharev <57486732+di-sukharev@users.noreply.github.com>
2023-03-28 11:16:17 +08:00
Esmerlin Joel Mieses
bdce94f2ac [FEAT](i18n): Add support for Spanish language (es_ES) (#58)
<3
2023-03-28 11:15:17 +08:00
Moret84
a3fade4d42 refactor(git.ts): use git rev-parse to get the root directory of the repository (#46)
The function getStagedFiles has been refactored to use git rev-parse to
get the root directory of the repository. This improves the reliability
of the function as it will work regardless of the current working
directory. The root directory is then passed to the git diff command to
get the list of staged files. With only the --relative flag, staged
diff(s) on files in a different same level directory as the current
working one would not be found by the command.
2023-03-24 10:11:50 +08:00
di-sukharev
83906fc704 docs(CONTRIBUTING.md): update branch name in step 6 from master to dev 2023-03-21 18:23:38 +08:00
di-sukharev
e7f7bfc2bd 1.1.22 2023-03-21 15:52:54 +08:00
di-sukharev
3885ae5893 1.1.21 2023-03-21 15:52:50 +08:00
di-sukharev
b8e05a5852 refactor(cli.ts): comment out checkIsLatestVersion function call
chore(checkIsLatestVersion.ts): add import for chalk library
2023-03-21 15:52:32 +08:00
di-sukharev
677b7ecad9 1.1.20 2023-03-21 15:36:20 +08:00
di-sukharev
6ab06f9db3 1.1.19 2023-03-21 15:36:16 +08:00
di-sukharev
38ac20612b feat(cli.ts): check if the latest version of opencommit is being used on startup
feat(api.ts): add function to get the latest version of opencommit from unpkg.com

feat(commit.ts): add support for pushing to multiple remotes and prompt user to choose which remote to push to

feat(i18n): add support for Portuguese (Brazil) language and translation for commit messages in pt_br.json

feat(utils): add checkIsLatestVersion function to check if the current version is the latest version
fix(git.ts): add --relative flag to getStagedFiles function to return relative paths
2023-03-21 15:36:05 +08:00
di-sukharev
54b8ba7419 chore(package.json): update version to 1.1.18
refactor(commit.ts): rename getStagedGitDiff to getChangedFiles and remove unused import of generateCommitMessageWithChatCompletion
refactor(commit.ts): reformat import statements for better readability

refactor(commit.ts): reformat code for better readability
feat(commit.ts): add support for selecting remote to push to when multiple remotes are available

refactor(commit.ts): remove unnecessary code and fix formatting
feat(commit.ts): add support for extraArgs parameter in generateCommitMessageFromGitDiff function

chore(checkIsLatestVersion.ts): update console message for clarity and readability
fix(git.ts): handle empty output from git command
fix(git.ts): add -- argument to git diff command to handle file names with leading hyphens
refactor(mergeStrings.ts): remove unnecessary whitespace and add missing semicolon
2023-03-21 15:35:04 +08:00
jessicakuijer
ff81d7e1ca feat(commit.ts) : Suggestion for the user to help how to choose where to push code on remotes (#20)
* 🔨 refactor(commit.ts): rename function to getGitRemotes and improve comments
This commit renames the function to getGitRemotes to better reflect its purpose. Additionally, the comments have been improved to better explain the function's behavior.

🚀 chore(commit.ts): add support for pushing to selected remote
This commit adds support for pushing to the selected remote. The user is prompted to choose a remote from the list of available remotes, and the selected remote is used to push the changes. The push command is run with the selected remote as an argument.

🐛 fix(commit.ts): fix formatting and add missing newline at end of file
This commit fixes the formatting of the code and adds a missing newline at the end of the file. The changes do not affect the functionality of the code.

* 🐛 fix(commit.ts): fix push command to include remote name
 feat(commit.ts): add support for multiple remotes
The push command was not including the remote name, which caused the push to fail. This has been fixed. Support for multiple remotes has been added, allowing the user to choose which remote to push to. If there is only one remote, the push command will automatically use that remote.
2023-03-21 15:14:13 +08:00
Nader Zouaoui
a6ccdb5f77 feature request: telling user when new version is available (#35)
*  feat(cli.ts): add checkIsLatestVersion function

This commit adds a new function that checks if the current version of OpenCommit is the latest version. The function uses the getOpenCommitLatestVersion function from the api module to get the latest version of OpenCommit. If the current version is not the latest version, a warning message is printed to the console, informing the user to update to the latest version to get the latest features and bug fixes.
2023-03-21 15:11:48 +08:00
Adriel Bento
fef8027959 feat(i18n): add support for Portuguese (Brazil) language (pt_br) (#34)
* feat(i18n): add support for Portuguese (Brazil) language (pt_br)
2023-03-21 15:09:48 +08:00
Moret84
0f48cc616e fix(git.ts): add relative flag to git diff command (#37)
The relative flag has been added to the git diff command in the
getStagedFiles function. This flag makes the output of the command
relative to the current working directory, which makes it easier to work
with the file paths and enables executing opencommit from anywhere in
the repository, not just from the root.
2023-03-21 15:08:46 +08:00
di-sukharev
b54ff02930 1.1.18 2023-03-21 15:06:47 +08:00
di-sukharev
7fb46de105 1.1.17 2023-03-21 15:06:43 +08:00
di-sukharev
2f6e98dc30 style(commit.ts): format code with prettier
refactor(commit.ts): add types to commit function parameters
fix(git.ts): handle empty string returned from getStagedFiles function

refactor(mergeStrings.ts): remove unnecessary blank line and add missing semicolon
2023-03-21 15:06:30 +08:00
di-sukharev
2aa6582c52 Merge remote-tracking branch 'origin/dev' 2023-03-21 14:51:44 +08:00
openefit
2acf833cd0 fix(generateCommitMessageFromGitDiff.ts): remove unnecessary character at the end of the line (#36) 2023-03-19 16:04:06 +08:00
Stuart van Beek
3f7025d50a feat: add support for .opencommitignore file (#22)
* feat: add support for .opencommitignore
2023-03-19 16:01:57 +08:00
Nader Zouaoui
d793bf1340 feat: Add support for extra args to be passed to the git commit command (#17)
* feat: Add support for extra args to be passed to the git commit command
2023-03-19 15:58:21 +08:00
di-sukharev
0092e92061 1.1.16 2023-03-17 17:51:42 +08:00
di-sukharev
0f33b74942 1.1.15 2023-03-17 17:51:36 +08:00
di-sukharev
8f0a32275e 1.1.14 2023-03-17 17:50:39 +08:00
di-sukharev
b3509e34d0 1.1.13 2023-03-17 14:20:51 +08:00
di-sukharev
5449d5cd61 1.1.12 2023-03-17 14:20:39 +08:00
di-sukharev
b0d27e62ba Merge remote-tracking branch 'origin/master' into dev 2023-03-17 14:19:23 +08:00
openefit
5ae52cd8bb 🚀 i18n: add internationalization support (#31)
* 🚀  i18n: add internationalization support
2023-03-17 13:57:26 +08:00
Sukharev
57baedd0b0 Feature: add staged files multiple selection (#6) (#27)
* feat: add staged files multiple selection (#6)
2023-03-17 13:53:22 +08:00
di-sukharev
d4fc651fec refactor(git.ts): remove unused code and simplify getDiff function
feat(git.ts): add message for excluded lock files in getDiff function
2023-03-16 23:41:08 +08:00
di-sukharev
04d40b5379 refactor(git.ts): rename someFilesExcludedMessage to showSomeFilesExcludedMessage
feat(git.ts): replace text prompt with outro prompt in showSomeFilesExcludedMessage function
feat(git.ts): add support for showing excluded files message in getStagedFiles, getChangedFiles, gitAdd and getDiff functions
2023-03-16 23:28:47 +08:00
di-sukharev
ec2e4c628c fix(commit.ts): fix typo in outro message
feat(commit.ts): add support for staging all files and generating commit message when no files are staged
feat(commit.ts): add support for committing changes when no files are staged and some files are changed but not staged
2023-03-16 23:15:56 +08:00
di-sukharev
c787329710 chore(README.md): fix capitalization of OpenCommit in setup and usage sections
docs(README.md): fix typo in Git hook section
2023-03-16 23:14:29 +08:00
Vladyslav Kapkan
0d1f72bdec Feature: add staged files multiple selection (#6)
* chore(package.json): add "@bdsqqq/try" dependency
* refactor(api.ts): remove unnecessary whitespace
* refactor(cli.ts): remove unused imports and variables

* refactor(commit.ts): rename getStagedGitDiff to getDif
* refactor(commit.ts): add getStagedFiles and getChangedFiles functions
* feat(commit.ts): add multiselect prompt to select files to stage
* feat(commit.ts): add gitAdd function to stage selected files
* feat(commit.ts): add trytm function to handle errors
* feat(commit.ts): add exitProgram function to exit the program with an error message if an error occurs during execution

* refactor(commit.ts): refactor commit function to handle unstaged files
* feat(commit.ts): add multiselect prompt to select files to add to commit when there are unstaged files

* feat(git.ts): add getStagedFiles function to get list of staged files
* feat(git.ts): add getChangedFiles function to get list of changed files
* feat(git.ts): add gitAdd function to add files to commit
* feat(git.ts): add getDif function to get diff of staged files

* refactor(commit.ts): replace exitProgram function with process.exit(1) to exit the program

* refactor(commit.ts): change message prompt to English in multiselect function

* chore(package.json): add prettier to format code
* refactor(api.ts): remove unnecessary whitespace and comments

* refactor(commit.ts): add missing semicolons and fix formatting
* feat(commit.ts): add support for selecting files to add to the commit when there are changed files but no staged files

* refactor(commit.ts): add isStageAllFlag parameter to commit function
* refactor(commit.ts): add whitespace to getDif function call
* refactor(commit.ts): add whitespace to generateCommitMessageFromGitDiff function call

* refactor(git.ts): reformat code for better readability
* chore(git.ts): add semicolons to the end of each statement

* chore(package.json): remove "@bdsqqq/try" dependency
* refactor(commit.ts): move trytm function to utils/trytm.ts
* refactor(commit.ts): add isStageAllFlag parameter to gitAdd function call in commit function
* refactor(commit.ts): remove getStagedGitDiff function call and use getStagedFiles function call instead
* refactor(commit.ts): add error handling to generateCommitMessageFromGitDiff function call in commit function

* refactor(prepare-commit-msg-hook.ts): rename getStagedGitDiff to getStagedFiles
* feat(prepare-commit-msg-hook.ts): add gitAdd function to stage changes before generating commit message
* refactor(prepare-commit-msg-hook.ts): use getDif function to get staged changes diff instead of staged.diff
* refactor(prepare-commit-msg-hook.ts): remove unnecessary if statement and return statement

* refactor(git.ts): remove StagedDiff interface and getStagedGitDiff function
* feat(git.ts): add support for untracked files in getChangedFiles function
* refactor(git.ts): rename stdout variable in getChangedFiles function
* refactor(git.ts): add excludeBigFilesFromDiff to getDif function
* feat(trytm.ts): add trytm utility function for handling promises with try-catch block

* fix(commit.ts): add missing function call parentheses in return statement

* refactor(commit.ts): remove unused variable generateCommitResponse
* refactor(commit.ts): exit process with code 0 after successful commit

* fix(commit.ts): add check for no changes detected before opening commit prompt

* fix(commit.ts): fix typo in function name from getDif to getDiff
* fix(prepare-commit-msg-hook.ts): fix typo in function name from getDif to getDiff
* refactor(git.ts): rename getDif function to getDiff for consistency and clarity

* chore(git.ts): add excludeBigFilesFromDiff option to getStagedFiles function

* chore(git.ts): import text function from @clack/prompts package

* refactor(git.ts): remove excludeBigFilesFromDiff constant and filter out .lock files from getStagedFiles and getChangedFiles functions
* feat(git.ts): add error message when all staged files are .lock files
* feat(git.ts): add error message when all changed files are .lock files
* feat(git.ts): add warning message when some files are .lock files and excluded from git add and git diff

* refactor(git.ts): add filter to remove empty strings from returned array in getStagedFiles and getChangedFiles functions

* refactor(commit.ts): pass isStageAllFlag to getChangedFiles function
* fix(commit.ts): handle errorStagedFiles and errorChangedFiles variables

* fix(git.ts): filter out empty strings from excludedFiles array
* feat(git.ts): add isStageAllFlag parameter to getChangedFiles function to handle git add --all command

* refactor(git.ts): remove console.log statement from getChangedFiles function and refactor code to improve readability

* refactor(commit.ts): remove unnecessary parameter from getChangedFiles function call
* refactor(git.ts): remove isStageAllFlag parameter from getChangedFiles function and add check for .lock files in the returned files list

* refactor(commit.ts): remove unnecessary line breaks and whitespace
* refactor(commit.ts): remove unnecessary parentheses in function calls

* feat(git.ts): add someFilesExcludedMessage function to display excluded files message
* refactor(git.ts): use someFilesExcludedMessage function instead of text function in getChangedFiles, gitAdd, and getDiff functions

* refactor(git.ts): extract someFilesExcludedMessage function to handle excluded files message
* fix(git.ts): use someFilesExcludedMessage function instead of throwing an error when all staged files are excluded files

* refactor(git.ts): pad excluded files list with 5 spaces

* refactor(git.ts): remove unnecessary padStart method call in someFilesExcludedMessage function
* chore(git.ts): update someFilesExcludedMessage function to improve readability

---------

Co-authored-by: Sukharev <57486732+di-sukharev@users.noreply.github.com>
2023-03-16 22:41:21 +08:00
di-sukharev
5d0c69e849 1.1.11 2023-03-16 11:48:42 +08:00
di-sukharev
9754442efa chore(README.md): update Twitter handle in header 2023-03-16 11:48:31 +08:00
di-sukharev
a619cd1f78 docs(README.md): update payment information to include GPT-4 cost comparison 2023-03-15 17:35:25 +08:00
di-sukharev
1fc4a6b6c0 1.1.10 2023-03-15 17:33:55 +08:00
di-sukharev
798bddba81 docs(README.md): remove outdated information about minimum supported version of Node.js 2023-03-15 17:33:43 +08:00
di-sukharev
42ed2a31f4 fix(commit.ts): remove '-u' and 'origin' arguments from git push command to push to the current branch 2023-03-15 14:51:26 +08:00
di-sukharev
571e1e9d8f chore(TODO.md): mark batch small files in one request as completed
refactor(TODO.md): rephrase optimize prompt TODO to suggest exploring cleaner alternatives to prompt
2023-03-15 14:49:38 +08:00
di-sukharev
bd8de7a8ea feat(commit.ts): add '-u origin HEAD' flag to 'git push' command to set upstream branch and push current branch to it 2023-03-15 14:46:15 +08:00
36 changed files with 682 additions and 170 deletions

View File

@@ -1,6 +1,6 @@
# Contribution Guidelines
Thank you for considering contributing to the project. Let's shake it baby.
Thanks for considering contributing to the project.
## How to contribute
@@ -9,7 +9,7 @@ Thank you for considering contributing to the project. Let's shake it baby.
3. Create a new branch for your changes.
4. Make your changes and commit them with descriptive commit messages.
5. Push your changes to your forked repository.
6. Create a pull request from your branch to the `master` branch.
6. Create a pull request from your branch to the `dev` branch.
## Getting started
@@ -28,9 +28,9 @@ Use the library to generate commits, stage the files and run `npm run dev` :)
If you encounter any issues while using the project, please report them on the GitHub issue tracker. When reporting issues, please include as much information as possible, such as steps to reproduce the issue, expected behavior, and actual behavior.
## Contact us
## Contacts
If you have any questions about contributing to the project, please contact us by [creating an issue](https://github.com/di-sukharev/open-commit/issues) on the GitHub issue tracker.
If you have any questions about contributing to the project, please contact by [creating an issue](https://github.com/di-sukharev/open-commit/issues) on the GitHub issue tracker.
## License

3
.github/TODO.md vendored
View File

@@ -5,5 +5,6 @@
- [] make bundle smaller by properly configuring esbuild
- [] [build for both mjs and cjs](https://snyk.io/blog/best-practices-create-modern-npm-package/)
- [] do // TODOs in the code
- [] batch small files in one request
- [x] batch small files in one request
- [] add tests
- [] optimize prompt, maybe no prompt would be cleaner

4
.gitignore vendored
View File

@@ -10,4 +10,6 @@ application.log
logfile.log
uncaughtExceptions.log
.vscode
src/*.json
src/*.json
.idea
test.ts

View File

@@ -2,7 +2,7 @@
<div>
<img src=".github/logo-grad.svg" alt="OpenCommit logo"/>
<h1 align="center">OpenCommit</h1>
<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 align="center">Follow the bird <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>
<h2>GPT CLI to auto-generate impressive commits in 1 second</h2>
@@ -20,9 +20,7 @@ All the commits in this repo are done with OpenCommit — look into [the commits
## Setup
> The minimum supported version of Node.js is the latest v14. Check your Node.js version with `node --version`.
1. Install opencommit globally to use in any repository:
1. Install OpenCommit globally to use in any repository:
```sh
npm install -g opencommit
@@ -30,7 +28,7 @@ All the commits in this repo are done with OpenCommit — look into [the commits
2. Get your API key from [OpenAI](https://platform.openai.com/account/api-keys). Make sure you add payment details, so API works.
3. Set the key to opencommit config:
3. Set the key to OpenCommit config:
```sh
opencommit config set OPENAI_API_KEY=<your_api_key>
@@ -40,7 +38,7 @@ All the commits in this repo are done with OpenCommit — look into [the commits
## Usage
You can call `opencommit` directly to generate a commit message for your staged changes:
You can call OpenCommit directly to generate a commit message for your staged changes:
```sh
git add <files...>
@@ -86,9 +84,53 @@ To remove description:
oc config set description=false
```
### Internationalization support
To specify the language used to generate commit messages:
```sh
# de, German ,Deutsch
oc config set language=de
oc config set language=German
oc config set language=Deutsch
# fr, French, française
oc config set language=fr
oc config set language=French
oc config set language=française
```
The default language set is **English**
All available languages are currently listed in the [i18n](https://github.com/di-sukharev/opencommit/tree/master/src/i18n) folder
### Git flags
The `opencommit` or `oc` commands can be used in place of the `git commit -m "${generatedMessage}"` command. This means that any regular flags that are used with the `git commit` command will also be applied when using `opencommit` or `oc`.
```sh
oc --no-verify
```
is translated to :
```sh
git commit -m "${generatedMessage}" --no-verify
```
### Ignore files
You can ignore files from submission to OpenAI by creating a `.opencommitignore` file. For example:
```ignorelang
path/to/large-asset.zip
**/*.jpg
```
This is useful for preventing opencommit from uploading artifacts and large files.
By default, opencommit ignores files matching: `*-lock.*` and `*.lock`
## Git hook
You can set opencommit as Git [`prepare-commit-msg`](https://git-scm.com/docs/githooks#_prepare_commit_msg) hook. Hook integrates with you IDE Source Control and allows you edit the message before commit.
You can set OpenCommit as Git [`prepare-commit-msg`](https://git-scm.com/docs/githooks#_prepare_commit_msg) hook. Hook integrates with you IDE Source Control and allows you edit the message before commit.
To set the hook:
@@ -113,4 +155,4 @@ Or follow the process of your IDE Source Control feature, when it calls `git com
## Payments
You pay for your own requests to OpenAI API. OpenCommit uses ChatGPT official model, that is ~10x times cheaper than GPT-3.
You pay for your own requests to OpenAI API. OpenCommit uses ChatGPT official model, that is ~10x times cheaper than GPT-3 and ~6x times cheaper than GPT-4.

24
package-lock.json generated
View File

@@ -1,19 +1,20 @@
{
"name": "opencommit",
"version": "1.1.9",
"version": "1.1.35",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "opencommit",
"version": "1.1.9",
"license": "ISC",
"version": "1.1.35",
"license": "MIT",
"dependencies": {
"@clack/prompts": "^0.6.1",
"axios": "^1.3.4",
"chalk": "^5.2.0",
"cleye": "^1.3.2",
"execa": "^7.0.0",
"ignore": "^5.2.4",
"ini": "^3.0.1",
"inquirer": "^9.1.4",
"openai": "^3.2.1"
@@ -31,6 +32,7 @@
"dotenv": "^16.0.3",
"esbuild": "^0.15.18",
"eslint": "^8.28.0",
"prettier": "^2.8.4",
"ts-node": "^10.9.1",
"typescript": "^4.9.3"
}
@@ -1879,7 +1881,6 @@
"version": "5.2.4",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
"integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==",
"dev": true,
"engines": {
"node": ">= 4"
}
@@ -2497,6 +2498,21 @@
"node": ">= 0.8.0"
}
},
"node_modules/prettier": {
"version": "2.8.4",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.4.tgz",
"integrity": "sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw==",
"dev": true,
"bin": {
"prettier": "bin-prettier.js"
},
"engines": {
"node": ">=10.13.0"
},
"funding": {
"url": "https://github.com/prettier/prettier?sponsor=1"
}
},
"node_modules/proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",

View File

@@ -1,6 +1,6 @@
{
"name": "opencommit",
"version": "1.1.9",
"version": "1.1.35",
"description": "GPT CLI to auto-generate impressive commits in 1 second. Killing lame commits with AI 🤯🔫",
"keywords": [
"git",
@@ -24,7 +24,7 @@
},
"type": "module",
"author": "https://github.com/di-sukharev",
"license": "ISC",
"license": "MIT",
"files": [
"out/**/*"
],
@@ -42,7 +42,8 @@
"dev": "ts-node ./src/cli.ts",
"build": "rimraf out && esbuild ./src/cli.ts --bundle --outfile=out/cli.cjs --format=cjs --platform=node",
"deploy": "npm run build && npm version patch && npm publish --tag latest",
"lint": "eslint src --ext ts && tsc --noEmit"
"lint": "eslint src --ext ts && tsc --noEmit",
"format": "prettier --write src"
},
"devDependencies": {
"@types/ini": "^1.3.31",
@@ -53,6 +54,7 @@
"dotenv": "^16.0.3",
"esbuild": "^0.15.18",
"eslint": "^8.28.0",
"prettier": "^2.8.4",
"ts-node": "^10.9.1",
"typescript": "^4.9.3"
},
@@ -62,6 +64,7 @@
"chalk": "^5.2.0",
"cleye": "^1.3.2",
"execa": "^7.0.0",
"ignore": "^5.2.4",
"ini": "^3.0.1",
"inquirer": "^9.1.4",
"openai": "^3.2.1"

View File

@@ -1,4 +1,4 @@
export enum COMMANDS {
config = 'config',
hook = 'hook'
hook = 'hook',
config = 'config'
}

View File

@@ -53,7 +53,10 @@ class OpenAi {
} catch (error: unknown) {
outro(`${chalk.red('✖')} ${error}`);
if (axios.isAxiosError<{ error?: { message: string } }>(error) && error.response?.status === 401) {
if (
axios.isAxiosError<{ error?: { message: string } }>(error) &&
error.response?.status === 401
) {
const openAiError = error.response.data.error;
if (openAiError?.message) outro(openAiError.message);
@@ -67,4 +70,18 @@ class OpenAi {
};
}
export const getOpenCommitLatestVersion = async (): Promise<
string | undefined
> => {
try {
const { data } = await axios.get(
'https://unpkg.com/opencommit/package.json'
);
return data.version;
} catch (_) {
outro('Error while getting the latest version of opencommit');
return undefined;
}
};
export const api = new OpenAi();

View File

@@ -7,8 +7,9 @@ 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';
const rawArgv = process.argv.slice(2);
const extraArgs = process.argv.slice(2);
cli(
{
@@ -19,12 +20,13 @@ cli(
ignoreArgv: (type) => type === 'unknown-flag' || type === 'argument',
help: { description: packageJSON.description }
},
() => {
async () => {
// await checkIsLatestVersion();
if (isHookCalled) {
prepareCommitMessageHook();
} else {
commit();
commit(extraArgs);
}
},
rawArgv
extraArgs
);

View File

@@ -3,12 +3,33 @@ import {
GenerateCommitMessageErrorEnum,
generateCommitMessageWithChatCompletion
} from '../generateCommitMessageFromGitDiff';
import { assertGitRepo, getStagedGitDiff } from '../utils/git';
import { spinner, confirm, outro, isCancel, intro } from '@clack/prompts';
import {
assertGitRepo,
getChangedFiles,
getDiff,
getStagedFiles,
gitAdd
} from '../utils/git';
import {
spinner,
confirm,
outro,
isCancel,
intro,
multiselect,
select
} from '@clack/prompts';
import chalk from 'chalk';
import { trytm } from '../utils/trytm';
const getGitRemotes = async () => {
const { stdout } = await execa('git', ['remote']);
return stdout.split('\n').filter((remote) => Boolean(remote.trim()));
};
const generateCommitMessageFromGitDiff = async (
diff: string
diff: string,
extraArgs: string[]
): Promise<void> => {
await assertGitRepo();
@@ -41,59 +62,106 @@ ${chalk.grey('——————————————————')}`
);
const isCommitConfirmedByUser = await confirm({
message: 'Confirm the commit message'
message: 'Confirm the commit message?'
});
if (isCommitConfirmedByUser && !isCancel(isCommitConfirmedByUser)) {
const { stdout } = await execa('git', ['commit', '-m', commitMessage]);
const { stdout } = await execa('git', [
'commit',
'-m',
commitMessage,
...extraArgs
]);
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();
const remotes = await getGitRemotes();
pushSpinner.start('Running `git push`');
const { stdout } = await execa('git', ['push']);
pushSpinner.stop(`${chalk.green('✔')} successfully pushed all commits`);
if (remotes.length === 1) {
const isPushConfirmedByUser = await confirm({
message: 'Do you want to run `git push`?'
});
if (stdout) outro(stdout);
if (isPushConfirmedByUser && !isCancel(isPushConfirmedByUser)) {
const pushSpinner = spinner();
pushSpinner.start(`Running \`git push ${remotes[0]}\``);
const { stdout } = await execa('git', [
'push',
'--verbose',
remotes[0]
]);
pushSpinner.stop(
`${chalk.green('✔')} successfully pushed all commits to ${remotes[0]}`
);
if (stdout) outro(stdout);
} else {
outro('`git push` aborted');
process.exit(0);
}
} else {
const selectedRemote = (await select({
message: 'Choose a remote to push to',
options: remotes.map((remote) => ({ value: remote, label: remote }))
})) as string;
if (!isCancel(selectedRemote)) {
const pushSpinner = spinner();
pushSpinner.start(`Running \`git push ${selectedRemote}\``);
const { stdout } = await execa('git', ['push', selectedRemote]);
pushSpinner.stop(
`${chalk.green(
'✔'
)} successfully pushed all commits to ${selectedRemote}`
);
if (stdout) outro(stdout);
} else outro(`${chalk.gray('✖')} process cancelled`);
}
} else outro(`${chalk.gray('✖')} process cancelled`);
}
};
export async function commit(isStageAllFlag = false) {
intro('open-commit');
export async function commit(
extraArgs: string[] = [],
isStageAllFlag: Boolean = false
) {
if (isStageAllFlag) {
const changedFiles = await getChangedFiles();
const stagedFilesSpinner = spinner();
stagedFilesSpinner.start('Counting staged files');
const staged = await getStagedGitDiff(isStageAllFlag);
if (changedFiles) await gitAdd({ files: changedFiles });
else {
outro('No changes detected, write some code and run `oc` again');
process.exit(1);
}
}
if (!staged && isStageAllFlag) {
outro(
`${chalk.red(
'No changes detected'
)} — write some code, stage the files ${chalk
.hex('0000FF')
.bold('`git add .`')} and rerun ${chalk
.hex('0000FF')
.bold('`oc`')} command.`
);
const [stagedFiles, errorStagedFiles] = await trytm(getStagedFiles());
const [changedFiles, errorChangedFiles] = await trytm(getChangedFiles());
if (!changedFiles?.length && !stagedFiles?.length) {
outro(chalk.red('No changes detected'));
process.exit(1);
}
if (!staged) {
outro(
`${chalk.red('Nothing to commit')} — stage the files ${chalk
.hex('0000FF')
.bold('`git add .`')} and rerun ${chalk
.hex('0000FF')
.bold('`oc`')} command.`
);
intro('open-commit');
if (errorChangedFiles ?? errorStagedFiles) {
outro(`${chalk.red('✖')} ${errorChangedFiles ?? errorStagedFiles}`);
process.exit(1);
}
const stagedFilesSpinner = spinner();
stagedFilesSpinner.start('Counting staged files');
if (!stagedFiles.length) {
stagedFilesSpinner.stop('No files are staged');
const isStageAllAndCommitConfirmedByUser = await confirm({
message: 'Do you want to stage all files and generate commit message?'
@@ -103,17 +171,45 @@ export async function commit(isStageAllFlag = false) {
isStageAllAndCommitConfirmedByUser &&
!isCancel(isStageAllAndCommitConfirmedByUser)
) {
await commit(true);
await commit(extraArgs, true);
process.exit(1);
}
if (stagedFiles.length === 0 && changedFiles.length > 0) {
const files = (await multiselect({
message: chalk.cyan('Select the files you want to add to the commit:'),
options: changedFiles.map((file) => ({
value: file,
label: file
}))
})) as string[];
if (isCancel(files)) process.exit(1);
await gitAdd({ files });
}
await commit(extraArgs, false);
process.exit(1);
}
stagedFilesSpinner.stop(
`${staged.files.length} staged files:\n${staged.files
`${stagedFiles.length} staged files:\n${stagedFiles
.map((file) => ` ${file}`)
.join('\n')}`
);
await generateCommitMessageFromGitDiff(staged.diff);
const [, generateCommitError] = await trytm(
generateCommitMessageFromGitDiff(
await getDiff({ files: stagedFiles }),
extraArgs
)
);
if (generateCommitError) {
outro(`${chalk.red('✖')} ${generateCommitError}`);
process.exit(1);
}
process.exit(0);
}

View File

@@ -6,11 +6,13 @@ import { homedir } from 'os';
import { intro, outro } from '@clack/prompts';
import chalk from 'chalk';
import { COMMANDS } from '../CommandsEnum';
import { getI18nLocal } from '../i18n';
export enum CONFIG_KEYS {
OPENAI_API_KEY = 'OPENAI_API_KEY',
description = 'description',
emoji = 'emoji'
emoji = 'emoji',
language = 'language'
}
export enum CONFIG_MODES {
@@ -27,6 +29,7 @@ const validateConfig = (
outro(
`${chalk.red('✖')} Unsupported config key ${key}: ${validationMessage}`
);
process.exit(1);
}
};
@@ -47,6 +50,7 @@ export const configValidators = {
return value;
},
[CONFIG_KEYS.description](value: any) {
validateConfig(
CONFIG_KEYS.description,
@@ -56,6 +60,7 @@ export const configValidators = {
return value;
},
[CONFIG_KEYS.emoji](value: any) {
validateConfig(
CONFIG_KEYS.emoji,
@@ -64,6 +69,15 @@ export const configValidators = {
);
return value;
},
[CONFIG_KEYS.language](value: any) {
validateConfig(
CONFIG_KEYS.language,
getI18nLocal(value),
`${value} is not supported yet`
);
return getI18nLocal(value);
}
};

33
src/commands/githook.ts Normal file → Executable file
View File

@@ -7,10 +7,27 @@ import chalk from 'chalk';
import { intro, outro } from '@clack/prompts';
import { COMMANDS } from '../CommandsEnum.js';
const HOOK_NAME = 'prepare-commit-msg';
const SYMLINK_URL = `.git/hooks/${HOOK_NAME}`;
const platform = process.platform;
export const isHookCalled = process.argv[1].endsWith(`/${SYMLINK_URL}`);
let separator = '';
switch (platform) {
case 'win32': // Windows
separator = path.sep;
break;
case 'darwin': // macOS
separator = '';
break;
case 'linux': // Linux
separator = '';
break;
default:
throw new Error(`Unsupported platform: ${platform}`);
}
const HOOK_NAME = 'prepare-commit-msg';
const SYMLINK_URL = path.join(separator, '.git', 'hooks', HOOK_NAME);
export const isHookCalled = process.argv[1].endsWith(`${SYMLINK_URL}`);
const isHookExists = existsSync(SYMLINK_URL);
@@ -28,7 +45,7 @@ export const hookCommand = command(
const { setUnset: mode } = argv._;
if (mode === 'set') {
intro(`setting opencommit as '${HOOK_NAME}' hook`);
intro(`setting OpenCommit as '${HOOK_NAME}' hook`);
if (isHookExists) {
let realPath;
@@ -40,7 +57,7 @@ export const hookCommand = command(
}
if (realPath === HOOK_URL)
return outro(`opencommit is already set as '${HOOK_NAME}'`);
return outro(`OpenCommit is already set as '${HOOK_NAME}'`);
throw new Error(
`Different ${HOOK_NAME} is already set. Remove it before setting opencommit as '${HOOK_NAME}' hook.`
@@ -55,18 +72,18 @@ export const hookCommand = command(
}
if (mode === 'unset') {
intro(`unsetting opencommit as '${HOOK_NAME}' hook`);
intro(`unsetting OpenCommit as '${HOOK_NAME}' hook`);
if (!isHookExists) {
return outro(
`opencommit wasn't previously set as '${HOOK_NAME}' hook, nothing to remove`
`OpenCommit wasn't previously set as '${HOOK_NAME}' hook, nothing to remove`
);
}
const realpath = await fs.realpath(SYMLINK_URL);
if (realpath !== HOOK_URL) {
return outro(
`opencommit wasn't previously set as '${HOOK_NAME}' hook, but different hook was, if you want to remove it — do it manually`
`OpenCommit wasn't previously set as '${HOOK_NAME}' hook, but different hook was, if you want to remove it — do it manually`
);
}

View File

@@ -1,7 +1,7 @@
import fs from 'fs/promises';
import chalk from 'chalk';
import { intro, outro } from '@clack/prompts';
import { getStagedGitDiff } from '../utils/git';
import { intro, outro, spinner } from '@clack/prompts';
import { getChangedFiles, getDiff, getStagedFiles, gitAdd } from '../utils/git';
import { getConfig } from './config';
import { generateCommitMessageWithChatCompletion } from '../generateCommitMessageFromGitDiff';
@@ -17,7 +17,17 @@ export const prepareCommitMessageHook = async () => {
if (commitSource) return;
const staged = await getStagedGitDiff();
const stagedFiles = await getStagedFiles();
const changedFiles = await getChangedFiles();
if (!stagedFiles && !changedFiles) {
outro('No changes detected, write some code and run `oc` again');
process.exit(1);
}
if (!stagedFiles && changedFiles) await gitAdd({ files: changedFiles });
const staged = await getStagedFiles();
if (!staged) return;
@@ -31,11 +41,15 @@ export const prepareCommitMessageHook = async () => {
);
}
const spin = spinner();
spin.start('Generating commit message');
const commitMessage = await generateCommitMessageWithChatCompletion(
staged.diff
await getDiff({ files: staged })
);
if (typeof commitMessage !== 'string') throw new Error(commitMessage.error);
if (typeof commitMessage !== 'string') {
spin.stop('Error');
throw new Error(commitMessage.error);
} else spin.stop('Done');
const fileContent = await fs.readFile(messageFilePath);

View File

@@ -4,54 +4,54 @@ import {
} from 'openai';
import { api } from './api';
import { getConfig } from './commands/config';
import { mergeStrings } from './utils/mergeStrings';
import { mergeDiffs } from './utils/mergeDiffs';
import { i18n, I18nLocals } from './i18n';
import { tokenCount } from './utils/tokenCount';
const config = getConfig();
const translation = i18n[(config?.language as I18nLocals) || 'en'];
const INIT_MESSAGES_PROMPT: Array<ChatCompletionRequestMessage> = [
{
role: ChatCompletionRequestMessageRoleEnum.System,
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. I'll send you an output of 'git diff --staged' command, and you convert it into a commit message. ${
config?.emoji
? 'Use Gitmoji convention to preface the commit'
: 'Do not preface the commit with anything'
}, use the present tense. ${
config?.description
? 'Add a short description of what commit is about 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."
}`
// 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 why a change was done. I'll send you an output of 'git diff --staged' command, and you convert it into a commit message.
${config?.emoji? 'Use GitMoji convention to preface the commit.': 'Do not preface the commit with anything.'}
${config?.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
index ad4db42..f3b18a9 100644
--- a/src/server.ts
+++ b/src/server.ts
@@ -10,7 +10,7 @@ import {
initWinstonLogger();
const app = express();
-const port = 7799;
+const PORT = 7799;
app.use(express.json());
@@ -34,6 +34,6 @@ app.use((_, res, next) => {
// ROUTES
app.use(PROTECTED_ROUTER_URL, protectedRouter);
-app.listen(port, () => {
- console.log(\`Server listening on port \${port}\`);
+app.listen(process.env.PORT || PORT, () => {
+ console.log(\`Server listening on port \${PORT}\`);
});`
index ad4db42..f3b18a9 100644
--- a/src/server.ts
+++ b/src/server.ts
@@ -10,7 +10,7 @@
import {
initWinstonLogger();
const app = express();
-const port = 7799;
+const PORT = 7799;
app.use(express.json());
@@ -34,6 +34,6 @@
app.use((_, res, next) => {
// ROUTES
app.use(PROTECTED_ROUTER_URL, protectedRouter);
-app.listen(port, () => {
- console.log(\`Server listening on port \${port}\`);
+app.listen(process.env.PORT || PORT, () => {
+ console.log(\`Server listening on port \${PORT}\`);
});`
},
{
role: ChatCompletionRequestMessageRoleEnum.Assistant,
// prettier-ignore
content: `${config?.emoji ? '🐛 ' : ''}fix(server.ts): change port variable case from lowercase port to uppercase PORT
${config?.emoji ? '✨ ' : ''}feat(server.ts): add support for process.env.PORT environment variable
${config?.description ? 'The port variable is now named PORT, which improves consistency with the naming conventions as PORT is a constant. Support for an environment variable allows the application to be more flexible as it can now run on any available port specified via the process.env.PORT environment variable.' : ''}`
content: `${config?.emoji ? '🐛 ' : ''}${translation.commitFix}
${config?.emoji ? ' ' : ''}${translation.commitFeat}
${config?.description ? translation.commitDescription : ''}`
}
];
@@ -79,8 +79,8 @@ interface GenerateCommitMessageError {
}
const INIT_MESSAGES_PROMPT_LENGTH = INIT_MESSAGES_PROMPT.map(
(msg) => msg.content
).join('').length;
(msg) => tokenCount(msg.content) + 4
).reduce((a, b) => a + b, 0);
const MAX_REQ_TOKENS = 3900 - INIT_MESSAGES_PROMPT_LENGTH;
@@ -88,8 +88,11 @@ export const generateCommitMessageWithChatCompletion = async (
diff: string
): Promise<string | GenerateCommitMessageError> => {
try {
if (diff.length >= MAX_REQ_TOKENS) {
const commitMessagePromises = getCommitMsgsPromisesFromFileDiffs(diff);
if (tokenCount(diff) >= MAX_REQ_TOKENS) {
const commitMessagePromises = getCommitMsgsPromisesFromFileDiffs(
diff,
MAX_REQ_TOKENS
);
const commitMessages = await Promise.all(commitMessagePromises);
@@ -109,22 +112,28 @@ export const generateCommitMessageWithChatCompletion = async (
}
};
function getMessagesPromisesByLines(fileDiff: string, separator: string) {
const lineSeparator = '\n@@';
const [fileHeader, ...fileDiffByLines] = fileDiff.split(lineSeparator);
function getMessagesPromisesByChangesInFile(
fileDiff: string,
separator: string,
maxChangeLength: number
) {
const hunkHeaderSeparator = '@@ ';
const [fileHeader, ...fileDiffByLines] = fileDiff.split(hunkHeaderSeparator);
// merge multiple line-diffs into 1 to save tokens
const mergedLines = mergeStrings(
fileDiffByLines.map((line) => lineSeparator + line),
MAX_REQ_TOKENS
const mergedChanges = mergeDiffs(
fileDiffByLines.map((line) => hunkHeaderSeparator + line),
maxChangeLength
);
const lineDiffsWithHeader = mergedLines.map(
(d) => fileHeader + lineSeparator + d
const lineDiffsWithHeader = mergedChanges.map(
(change) => fileHeader + change
);
const commitMsgsFromFileLineDiffs = lineDiffsWithHeader.map((d) => {
const messages = generateCommitMessageChatCompletionPrompt(separator + d);
const commitMsgsFromFileLineDiffs = lineDiffsWithHeader.map((lineDiff) => {
const messages = generateCommitMessageChatCompletionPrompt(
separator + lineDiff
);
return api.generateCommitMessage(messages);
});
@@ -132,20 +141,27 @@ function getMessagesPromisesByLines(fileDiff: string, separator: string) {
return commitMsgsFromFileLineDiffs;
}
function getCommitMsgsPromisesFromFileDiffs(diff: string) {
export function getCommitMsgsPromisesFromFileDiffs(
diff: string,
maxDiffLength: number
) {
const separator = 'diff --git ';
const diffByFiles = diff.split(separator).slice(1);
// merge multiple files-diffs into 1 prompt to save tokens
const mergedFilesDiffs = mergeStrings(diffByFiles, MAX_REQ_TOKENS);
const mergedFilesDiffs = mergeDiffs(diffByFiles, maxDiffLength);
const commitMessagePromises = [];
for (const fileDiff of mergedFilesDiffs) {
if (fileDiff.length >= MAX_REQ_TOKENS) {
if (tokenCount(fileDiff) >= maxDiffLength) {
// if file-diff is bigger than gpt context — split fileDiff into lineDiff
const messagesPromises = getMessagesPromisesByLines(fileDiff, separator);
const messagesPromises = getMessagesPromisesByChangesInFile(
fileDiff,
separator,
maxDiffLength
);
commitMessagePromises.push(...messagesPromises);
} else {

6
src/i18n/de.json Normal file
View File

@@ -0,0 +1,6 @@
{
"localLanguage": "Deutsch",
"commitFix": "fix(server.ts): Ändere die Groß- und Kleinschreibung der Port-Variable von Kleinbuchstaben auf Großbuchstaben PORT.",
"commitFeat": "Funktion(server.ts): Unterstützung für die Umgebungsvariable process.env.PORT hinzufügen",
"commitDescription": "Die Port-Variable heißt jetzt PORT, was die Konsistenz mit den Namenskonventionen verbessert, da PORT eine Konstante ist. Die Unterstützung für eine Umgebungsvariable ermöglicht es der Anwendung, flexibler zu sein, da sie jetzt auf jedem verfügbaren Port laufen kann, der über die Umgebungsvariable process.env.PORT angegeben wird."
}

6
src/i18n/en.json Normal file
View File

@@ -0,0 +1,6 @@
{
"localLanguage": "english",
"commitFix": "fix(server.ts): change port variable case from lowercase port to uppercase PORT",
"commitFeat": "feat(server.ts): add support for process.env.PORT environment variable",
"commitDescription": "The port variable is now named PORT, which improves consistency with the naming conventions as PORT is a constant. Support for an environment variable allows the application to be more flexible as it can now run on any available port specified via the process.env.PORT environment variable."
}

6
src/i18n/es_ES.json Normal file
View File

@@ -0,0 +1,6 @@
{
"localLanguage": "spanish",
"commitFix": "fix(server.ts): cambiar la variable port de minúsculas a mayúsculas PORT",
"commitFeat": "feat(server.ts): añadir soporte para la variable de entorno process.env.PORT",
"commitDescription": "La variable port ahora se llama PORT, lo que mejora la coherencia con las convenciones de nomenclatura, ya que PORT es una constante. El soporte para una variable de entorno permite que la aplicación sea más flexible, ya que ahora puede ejecutarse en cualquier puerto disponible especificado a través de la variable de entorno process.env.PORT."
}

6
src/i18n/fr.json Normal file
View File

@@ -0,0 +1,6 @@
{
"localLanguage": "française",
"commitFix": "corriger(server.ts) : changer la casse de la variable de port de minuscules à majuscules (PORT)",
"commitFeat": "fonctionnalité(server.ts) : ajouter la prise en charge de la variable d'environnement process.env.PORT",
"commitDescription": "La variable de port est maintenant nommée PORT, ce qui améliore la cohérence avec les conventions de nommage car PORT est une constante. La prise en charge d'une variable d'environnement permet à l'application d'être plus flexible car elle peut maintenant s'exécuter sur n'importe quel port disponible spécifié via la variable d'environnement process.env.PORT."
}

6
src/i18n/id_ID.json Normal file
View File

@@ -0,0 +1,6 @@
{
"localLanguage": "bahasa",
"commitFix": "fix(server.ts): mengubah huruf port variable dari huruf kecil ke huruf besar PORT",
"commitFeat": "feat(server.ts): menambahkan support di process.env.PORT environment variabel",
"commitDescription": "Port variabel bernama PORT, yang membantu konsistensi dengan memberi nama yaitu PORT yang konstan. Bantuan environment variabel membantu aplikasi lebih fleksibel, dan dapat di jalankan di port manapun yang tertulis pada process.env.PORT"
}

78
src/i18n/index.ts Normal file
View File

@@ -0,0 +1,78 @@
import en from '../i18n/en.json' assert { type: 'json' };
import de from '../i18n/de.json' assert { type: 'json' };
import fr from '../i18n/fr.json' assert { type: 'json' };
import it from '../i18n/it.json' assert { type: 'json' };
import ko from '../i18n/ko.json' assert { type: 'json' };
import zh_CN from '../i18n/zh_CN.json' assert { type: 'json' };
import zh_TW from '../i18n/zh_TW.json' assert { type: 'json' };
import ja from '../i18n/ja.json' assert { type: 'json' };
import pt_br from '../i18n/pt_br.json' assert { type: 'json' };
import vi_VN from '../i18n/vi_VN.json' assert { type: 'json' };
import es_ES from '../i18n/es_ES.json' assert { type: 'json' };
import sv from '../i18n/sv.json' assert { type: 'json' };
import nl from '../i18n/nl.json' assert { type: 'json' };
import ru from '../i18n/ru.json' assert { type: 'json' };
import id_ID from '../i18n/id_ID.json' assert { type: 'json' };
export enum I18nLocals {
'en' = 'en',
'zh_CN' = 'zh_CN',
'zh_TW' = 'zh_TW',
'ja' = 'ja',
'de' = 'de',
'fr' = 'fr',
'nl' = 'nl',
'it' = 'it',
'ko' = 'ko',
'pt_br' = 'pt_br',
'es_ES' = 'es_ES',
'sv' = 'sv',
'ru' = 'ru',
'id_ID' = 'id_ID'
}
export const i18n = {
en,
zh_CN,
zh_TW,
ja,
de,
fr,
it,
ko,
pt_br,
vi_VN,
es_ES,
sv,
id_ID,
nl,
ru
};
export const I18N_CONFIG_ALIAS: { [key: string]: string[] } = {
zh_CN: ['zh_CN', '简体中文', '中文', '简体'],
zh_TW: ['zh_TW', '繁體中文', '繁體'],
ja: ['ja', 'Japanese', 'にほんご'],
ko: ['ko', 'Korean', '한국어'],
de: ['de', 'German', 'Deutsch'],
fr: ['fr', 'French', 'française'],
it: ['it', 'Italian', 'italiano'],
nl: ['nl', 'Dutch', 'Nederlands'],
pt_br: ['pt_br', 'Portuguese', 'português'],
vi_VN: ['vi_VN', 'Vietnamese', 'tiếng Việt'],
en: ['en', 'English', 'english'],
es_ES: ['es_ES', 'Spanish', 'español'],
sv: ['sv', 'Swedish', 'Svenska'],
ru: ['ru', 'Russian', 'русский'],
id_ID: ['id_ID', 'Bahasa', 'bahasa']
};
export function getI18nLocal(value: string): string | boolean {
for (const key in I18N_CONFIG_ALIAS) {
const aliases = I18N_CONFIG_ALIAS[key];
if (aliases.includes(value)) {
return key;
}
}
return false;
}

6
src/i18n/it.json Normal file
View File

@@ -0,0 +1,6 @@
{
"localLanguage": "italiano",
"commitFix": "fix(server.ts): cambia il caso della variabile di porta da minuscolo port a maiuscolo PORT",
"commitFeat": "funzionalità(server.ts): aggiungi supporto per la variabile di ambiente process.env.PORT",
"commitDescription": "La variabile di porta è ora chiamata PORT, il che migliora la coerenza con le convenzioni di denominazione in quanto PORT è una costante. Il supporto per una variabile di ambiente consente all'applicazione di essere più flessibile poiché ora può essere eseguita su qualsiasi porta disponibile specificata tramite la variabile di ambiente process.env.PORT."
}

6
src/i18n/ja.json Normal file
View File

@@ -0,0 +1,6 @@
{
"localLanguage": "にほんご",
"commitFix": "修正(server.ts): ポート変数のケースを小文字のポートから大文字のPORTに変更",
"commitFeat": "新機能(server.ts): process.env.PORT環境変数のサポートを追加する",
"commitDescription": "ポート変数は現在PORTという名前になっており、PORTは定数であるため命名規則に一貫性があります。環境変数のサポートにより、アプリケーションはより柔軟になり、process.env.PORT環境変数で指定された任意の利用可能なポートで実行できるようになりまし"
}

6
src/i18n/ko.json Normal file
View File

@@ -0,0 +1,6 @@
{
"localLanguage": "한국어",
"commitFix": "fix(server.ts): 포트 변수를 소문자 port에서 대문자 PORT로 변경",
"commitFeat": "피트(server.ts): process.env.PORT 환경 변수 지원 추가",
"commitDescription": "포트 변수는 이제 PORT로 이름이 지정되어 상수인 PORT와 일관성 있는 이름 규칙을 따릅니다. 환경 변수 지원을 통해 애플리케이션은 이제 process.env.PORT 환경 변수로 지정된 사용 가능한 모든 포트에서 실행할 수 있으므로 더 유연해졌습니다."
}

6
src/i18n/nl.json Normal file
View File

@@ -0,0 +1,6 @@
{
"localLanguage": "Nederlands",
"commitFix": "fix(server.ts): verander poortvariabele van kleine letters poort naar hoofdletters PORT",
"commitFeat": "feat(server.ts): voeg ondersteuning toe voor process.env.PORT omgevingsvariabele",
"commitDescription": "De poortvariabele heet nu PORT, wat de consistentie met de naamgevingsconventies verbetert omdat PORT een constante is. Ondersteuning voor een omgevingsvariabele maakt de applicatie flexibeler, omdat deze nu kan draaien op elke beschikbare poort die is gespecificeerd via de process.env.PORT omgevingsvariabele."
}

6
src/i18n/pt_br.json Normal file
View File

@@ -0,0 +1,6 @@
{
"localLanguage": "português",
"commitFix": "fix(server.ts): altera o caso da variável de porta de port minúscula para PORT maiúscula",
"commitFeat": "feat(server.ts): adiciona suporte para a variável de ambiente process.env.PORT",
"commitDescription": "A variável de porta agora é denominada PORT, o que melhora a consistência com as convenções de nomenclatura, pois PORT é uma constante. O suporte para uma variável de ambiente permite que o aplicativo seja mais flexível, pois agora pode ser executado em qualquer porta disponível especificada por meio da variável de ambiente process.env.PORT."
}

6
src/i18n/ru.json Normal file
View File

@@ -0,0 +1,6 @@
{
"localLanguage": "русский",
"commitFix": "fix(server.ts): изменение регистра переменной порта с нижнего регистра port на верхний регистр PORT",
"commitFeat": "feat(server.ts): добавлена поддержка переменной окружения process.env.PORT",
"commitDescription": "Переменная port теперь называется PORT, что улучшает согласованность с соглашениями об именовании констант. Поддержка переменной окружения позволяет приложению быть более гибким, запускаясь на любом доступном порту, указанном с помощью переменной окружения process.env.PORT."
}

6
src/i18n/sv.json Normal file
View File

@@ -0,0 +1,6 @@
{
"localLanguage": "svenska",
"commitFix": "fixa(server.ts): ändra variabelnamnet för port från små bokstäver till stora bokstäver PORT",
"commitFeat": "nyhet(server.ts): lägg till stöd för process.env.PORT miljövariabel",
"commitDescription": "Variabeln som innehåller portnumret heter nu PORT vilket förbättrar konsekvensen med namngivningskonventionerna eftersom PORT är en konstant. Stöd för en miljövariabel gör att applikationen kan vara mer flexibel då den nu kan köras på vilken port som helst som specificeras via miljövariabeln process.env.PORT."
}

6
src/i18n/vi_VN.json Normal file
View File

@@ -0,0 +1,6 @@
{
"localLanguage": "vietnamese",
"commitFix": "fix(server.ts): thay đổi chữ viết thường của biến port thành chữ viết hoa PORT",
"commitFeat": "feat(server.ts): thêm hỗ trợ cho biến môi trường process.env.PORT",
"commitDescription": "Biến port đã được đổi tên thành PORT, giúp cải thiện tính nhất quán trong việc đặt tên theo quy ước vì PORT là một hằng số. Hỗ trợ cho biến môi trường cho phép ứng dụng linh hoạt hơn khi có thể chạy trên bất kỳ cổng nào được chỉ định thông qua biến môi trường process.env.PORT."
}

6
src/i18n/zh_CN.json Normal file
View File

@@ -0,0 +1,6 @@
{
"localLanguage": "简体中文",
"commitFix": "修复(server.ts)将端口变量从小写port改为大写PORT",
"commitFeat": "功能(server.ts)添加对process.env.PORT环境变量的支持",
"commitDescription": "现在端口变量被命名为PORT这提高了命名约定的一致性因为PORT是一个常量。环境变量的支持使应用程序更加灵活因为它现在可以通过process.env.PORT环境变量在任何可用端口上运行。"
}

6
src/i18n/zh_TW.json Normal file
View File

@@ -0,0 +1,6 @@
{
"localLanguage": "繁體中文",
"commitFix": "修正(server.ts)將端口變數從小寫端口改為大寫PORT",
"commitFeat": "功能(server.ts)新增對process.env.PORT環境變數的支援",
"commitDescription": "現在port變數已更名為PORT以符合命名慣例因為PORT是一個常量。支援環境變數可以使應用程序更靈活因為它現在可以通過process.env.PORT環境變數運行在任何可用端口上。"
}

View File

@@ -0,0 +1,23 @@
import { getOpenCommitLatestVersion } from '../api';
import currentPackage from '../../package.json' assert { type: 'json' };
import chalk from 'chalk';
export const checkIsLatestVersion = async () => {
const latestVersion = await getOpenCommitLatestVersion();
if (latestVersion) {
const currentVersion = currentPackage.version;
if (currentVersion !== latestVersion) {
console.warn(
chalk.yellow(
`
You are not using the latest stable version of OpenCommit with new features and bug fixes.
Current version: ${currentVersion}. Latest version: ${latestVersion}.
🚀 To update run: npm i -g opencommit@latest.
`
)
);
}
}
};

View File

@@ -1,5 +1,7 @@
import { execa } from 'execa';
import { spinner } from '@clack/prompts';
import { outro, spinner } from '@clack/prompts';
import { readFileSync } from 'fs';
import ignore, { Ignore } from 'ignore';
export const assertGitRepo = async () => {
try {
@@ -9,41 +11,91 @@ export const assertGitRepo = async () => {
}
};
const excludeBigFilesFromDiff = ['*-lock.*', '*.lock'].map(
(file) => `:(exclude)${file}`
);
// const excludeBigFilesFromDiff = ['*-lock.*', '*.lock'].map(
// (file) => `:(exclude)${file}`
// );
export interface StagedDiff {
files: string[];
diff: string;
}
export const getOpenCommitIgnore = (): Ignore => {
const ig = ignore();
export const getStagedGitDiff = async (
isStageAllFlag = false
): Promise<StagedDiff | null> => {
if (isStageAllFlag) {
const stageAllSpinner = spinner();
stageAllSpinner.start('Staging all changes');
await execa('git', ['add', '.']);
stageAllSpinner.stop('Done');
try {
ig.add(readFileSync('.opencommitignore').toString().split('\n'));
} catch (e) {}
return ig;
};
export const getStagedFiles = async (): Promise<string[]> => {
const { stdout: gitDir } = await execa('git', [
'rev-parse',
'--show-toplevel'
]);
const { stdout: files } = await execa('git', [
'diff',
'--name-only',
'--cached',
'--relative',
gitDir
]);
if (!files) return [];
const filesList = files.split('\n');
const ig = getOpenCommitIgnore();
const allowedFiles = filesList.filter((file) => !ig.ignores(file));
if (!allowedFiles) return [];
return allowedFiles.sort();
};
export const getChangedFiles = async (): Promise<string[]> => {
const { stdout: modified } = await execa('git', ['ls-files', '--modified']);
const { stdout: others } = await execa('git', [
'ls-files',
'--others',
'--exclude-standard'
]);
const files = [...modified.split('\n'), ...others.split('\n')].filter(
(file) => !!file
);
return files.sort();
};
export const gitAdd = async ({ files }: { files: string[] }) => {
const gitAddSpinner = spinner();
gitAddSpinner.start('Adding files to commit');
await execa('git', ['add', ...files]);
gitAddSpinner.stop('Done');
};
export const getDiff = async ({ files }: { files: string[] }) => {
const lockFiles = files.filter(
(file) => file.includes('.lock') || file.includes('-lock.')
);
if (lockFiles.length) {
outro(
`Some files are '.lock' files which are excluded by default from 'git diff'. No commit messages are generated for this files:\n${lockFiles.join(
'\n'
)}`
);
}
const diffStaged = ['diff', '--staged'];
const { stdout: files } = await execa('git', [
...diffStaged,
'--name-only',
...excludeBigFilesFromDiff
]);
if (!files) return null;
const filesWithoutLocks = files.filter(
(file) => !file.includes('.lock') && !file.includes('-lock.')
);
const { stdout: diff } = await execa('git', [
...diffStaged,
...excludeBigFilesFromDiff
'diff',
'--staged',
'--',
...filesWithoutLocks
]);
return {
files: files.split('\n').sort(),
diff
};
return diff;
};

View File

@@ -1,14 +1,17 @@
export function mergeStrings(arr: string[], maxStringLength: number): string[] {
import { tokenCount } from './tokenCount';
export function mergeDiffs(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) {
if (tokenCount(currentItem + item) <= maxStringLength) {
currentItem += item;
} else {
mergedArr.push(currentItem);
currentItem = item;
}
}
mergedArr.push(currentItem);
return mergedArr;
}

14
src/utils/tokenCount.ts Normal file
View File

@@ -0,0 +1,14 @@
// import { Tiktoken } from '@dqbd/tiktoken/lite';
// import cl100k_base from '@dqbd/tiktoken/encoders/cl100k_base.json' assert { type: 'json' };
export function tokenCount(content: string): number {
// const encoding = new Tiktoken(
// cl100k_base.bpe_ranks,
// cl100k_base.special_tokens,
// cl100k_base.pat_str
// );
// const tokens = encoding.encode(content);
// encoding.free();
return content.length / 2.7;
}

12
src/utils/trytm.ts Normal file
View File

@@ -0,0 +1,12 @@
export const trytm = async <T>(
promise: Promise<T>
): Promise<[T, null] | [null, Error]> => {
try {
const data = await promise;
return [data, null];
} catch (throwable) {
if (throwable instanceof Error) return [null, throwable];
throw throwable;
}
};

View File

@@ -1,6 +1,6 @@
{
"compilerOptions": {
"target": "ES2020",
"target": "ESNext",
"lib": ["ES5", "ES6"],
"module": "ESNext",