Compare commits
30 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ebbaff0628 | ||
|
|
4f164a31d1 | ||
|
|
a70a2b8a9f | ||
|
|
52bb719f4e | ||
|
|
c904a78cd9 | ||
|
|
22077399fd | ||
|
|
8ae2f7ddf1 | ||
|
|
b318d1d882 | ||
|
|
af0f2c1df4 | ||
|
|
c5ce50aaa3 | ||
|
|
c1756b85af | ||
|
|
dac1271782 | ||
|
|
1cc7a64f99 | ||
|
|
4deb7bca65 | ||
|
|
1a90485a10 | ||
|
|
48b8d9d7b2 | ||
|
|
7e60c68ba5 | ||
|
|
24adc16adf | ||
|
|
881f07eebe | ||
|
|
3a255a3ad9 | ||
|
|
9971b3c74e | ||
|
|
66a5695d89 | ||
|
|
fd22f713ed | ||
|
|
43dc5e6c2b | ||
|
|
3d42dde48c | ||
|
|
19f32ca57d | ||
|
|
c1070789fd | ||
|
|
1f0f44ede0 | ||
|
|
45aed936b1 | ||
|
|
e4f7e8dc80 |
BIN
.github/github-mark-white.png
vendored
|
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 2.8 KiB |
BIN
.github/logo-black.png
vendored
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 1.7 KiB |
14
.github/logo-grad.svg
vendored
@@ -1,13 +1 @@
|
||||
<svg width="78" height="75" viewBox="0 0 78 75" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M32.269 2.94345C34.6328 4.17458 36.5623 5.81371 38.0626 7.86409C37.7038 8.37105 37.3661 8.90001 37.0496 9.45094L37.0495 9.45091L37.0456 9.45797C35.2629 12.6805 34.3831 16.5345 34.3831 21V54C34.3831 58.4007 35.2636 62.2523 37.0435 65.5381L37.0433 65.5382L37.0496 65.5491C37.3661 66.1 37.7038 66.629 38.0626 67.1359C36.5622 69.1863 34.6328 70.8254 32.269 72.0565L32.2652 72.0586C29.2195 73.6786 25.5374 74.5 21.2 74.5C16.8638 74.5 13.1471 73.6791 10.0328 72.0575C6.98854 70.4377 4.62693 68.1096 2.94057 65.0635C1.31973 61.949 0.5 58.2664 0.5 54V21C0.5 16.6643 1.32072 12.9834 2.93951 9.93843C4.62596 6.89138 6.98794 4.56255 10.0329 2.94245C13.1472 1.32089 16.8639 0.5 21.2 0.5C25.5374 0.5 29.2195 1.32137 32.2652 2.94145L32.269 2.94345ZM38.6667 8.74806C38.9107 9.13077 39.1413 9.52635 39.3586 9.93481L39.3585 9.93484L39.3625 9.94203C41.047 12.9872 41.9 16.6336 41.9 20.9V54C41.9 58.266 41.0472 61.9477 39.3603 65.0619L39.3586 65.0652C39.1413 65.4736 38.9107 65.8692 38.6667 66.2519C38.4054 65.8665 38.1565 65.468 37.9199 65.0565C36.235 61.9435 35.3831 58.2635 35.3831 54V21C35.3831 16.6672 36.236 12.989 37.9187 9.94557C38.1556 9.53328 38.405 9.13412 38.6667 8.74806ZM39.2936 7.87926C40.8728 5.82164 42.8446 4.17787 45.2123 2.94436C48.3955 1.32076 52.1474 0.5 56.4831 0.5C60.8172 0.5 64.5319 1.3534 67.645 3.03964L67.6449 3.0397L67.6522 3.04345C70.7657 4.6651 73.1602 6.99537 74.8456 10.042C76.464 12.9676 77.3148 16.448 77.3792 20.5H69.3778C69.2917 16.5201 68.1674 13.3804 65.942 11.1517C63.6909 8.76341 60.5126 7.6 56.4831 7.6C52.4533 7.6 49.2164 8.72969 46.8349 11.0412L46.8348 11.0412L46.8296 11.0464C44.5081 13.3679 43.3831 16.6791 43.3831 20.9V54C43.3831 58.2218 44.5085 61.5622 46.8243 63.9482L46.8295 63.9536L46.8349 63.9588C49.2164 66.2703 52.4533 67.4 56.4831 67.4C60.5114 67.4 63.6898 66.2708 65.9421 63.9481C68.1656 61.657 69.2916 58.4862 69.3778 54.5H77.379C77.3138 58.4875 76.4638 61.9697 74.8444 64.9601C73.1588 68.0063 70.7636 70.3703 67.6486 72.0584C64.5346 73.6794 60.8185 74.5 56.4831 74.5C52.1474 74.5 48.3956 73.6793 45.2125 72.0557C42.8446 70.8222 40.8729 69.1784 39.2936 67.1207C39.6322 66.6146 39.9479 66.0865 40.2405 65.5365C42.0198 62.251 42.9 58.4 42.9 54V20.9C42.9 16.5014 42.0203 12.6824 40.2396 9.46166C39.9472 8.91234 39.6319 8.38486 39.2936 7.87926ZM11.8359 63.9427L11.8359 63.9427L11.841 63.9481C14.0918 66.2691 17.2355 67.4 21.2 67.4C25.2274 67.4 28.3768 66.2711 30.5644 63.9423C32.8103 61.5559 33.9 58.2177 33.9 54V21C33.9 16.7865 32.8123 13.4792 30.5643 11.1575C28.378 8.76316 25.2286 7.6 21.2 7.6C17.2326 7.6 14.088 8.76605 11.8384 11.1546C9.58856 13.4765 8.5 16.7848 8.5 21V54C8.5 58.2179 9.58979 61.5562 11.8359 63.9427Z" fill="url(#paint0_linear_498_146)" stroke="url(#paint1_linear_498_146)"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_498_146" x1="38.9416" y1="0" x2="38.9416" y2="75" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D33075"/>
|
||||
<stop offset="1" stop-color="#6157D8"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_498_146" x1="38.9416" y1="0" x2="38.9416" y2="75" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D33075"/>
|
||||
<stop offset="1" stop-color="#6157D8"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="78" height="75" fill="none" viewBox="0 0 78 75"><path fill="url(#paint0_linear_498_146)" stroke="url(#paint1_linear_498_146)" d="M32.269 2.94345C34.6328 4.17458 36.5623 5.81371 38.0626 7.86409C37.7038 8.37105 37.3661 8.90001 37.0496 9.45094L37.0495 9.45091L37.0456 9.45797C35.2629 12.6805 34.3831 16.5345 34.3831 21V54C34.3831 58.4007 35.2636 62.2523 37.0435 65.5381L37.0433 65.5382L37.0496 65.5491C37.3661 66.1 37.7038 66.629 38.0626 67.1359C36.5622 69.1863 34.6328 70.8254 32.269 72.0565L32.2652 72.0586C29.2195 73.6786 25.5374 74.5 21.2 74.5C16.8638 74.5 13.1471 73.6791 10.0328 72.0575C6.98854 70.4377 4.62693 68.1096 2.94057 65.0635C1.31973 61.949 0.5 58.2664 0.5 54V21C0.5 16.6643 1.32072 12.9834 2.93951 9.93843C4.62596 6.89138 6.98794 4.56255 10.0329 2.94245C13.1472 1.32089 16.8639 0.5 21.2 0.5C25.5374 0.5 29.2195 1.32137 32.2652 2.94145L32.269 2.94345ZM38.6667 8.74806C38.9107 9.13077 39.1413 9.52635 39.3586 9.93481L39.3585 9.93484L39.3625 9.94203C41.047 12.9872 41.9 16.6336 41.9 20.9V54C41.9 58.266 41.0472 61.9477 39.3603 65.0619L39.3586 65.0652C39.1413 65.4736 38.9107 65.8692 38.6667 66.2519C38.4054 65.8665 38.1565 65.468 37.9199 65.0565C36.235 61.9435 35.3831 58.2635 35.3831 54V21C35.3831 16.6672 36.236 12.989 37.9187 9.94557C38.1556 9.53328 38.405 9.13412 38.6667 8.74806ZM39.2936 7.87926C40.8728 5.82164 42.8446 4.17787 45.2123 2.94436C48.3955 1.32076 52.1474 0.5 56.4831 0.5C60.8172 0.5 64.5319 1.3534 67.645 3.03964L67.6449 3.0397L67.6522 3.04345C70.7657 4.6651 73.1602 6.99537 74.8456 10.042C76.464 12.9676 77.3148 16.448 77.3792 20.5H69.3778C69.2917 16.5201 68.1674 13.3804 65.942 11.1517C63.6909 8.76341 60.5126 7.6 56.4831 7.6C52.4533 7.6 49.2164 8.72969 46.8349 11.0412L46.8348 11.0412L46.8296 11.0464C44.5081 13.3679 43.3831 16.6791 43.3831 20.9V54C43.3831 58.2218 44.5085 61.5622 46.8243 63.9482L46.8295 63.9536L46.8349 63.9588C49.2164 66.2703 52.4533 67.4 56.4831 67.4C60.5114 67.4 63.6898 66.2708 65.9421 63.9481C68.1656 61.657 69.2916 58.4862 69.3778 54.5H77.379C77.3138 58.4875 76.4638 61.9697 74.8444 64.9601C73.1588 68.0063 70.7636 70.3703 67.6486 72.0584C64.5346 73.6794 60.8185 74.5 56.4831 74.5C52.1474 74.5 48.3956 73.6793 45.2125 72.0557C42.8446 70.8222 40.8729 69.1784 39.2936 67.1207C39.6322 66.6146 39.9479 66.0865 40.2405 65.5365C42.0198 62.251 42.9 58.4 42.9 54V20.9C42.9 16.5014 42.0203 12.6824 40.2396 9.46166C39.9472 8.91234 39.6319 8.38486 39.2936 7.87926ZM11.8359 63.9427L11.8359 63.9427L11.841 63.9481C14.0918 66.2691 17.2355 67.4 21.2 67.4C25.2274 67.4 28.3768 66.2711 30.5644 63.9423C32.8103 61.5559 33.9 58.2177 33.9 54V21C33.9 16.7865 32.8123 13.4792 30.5643 11.1575C28.378 8.76316 25.2286 7.6 21.2 7.6C17.2326 7.6 14.088 8.76605 11.8384 11.1546C9.58856 13.4765 8.5 16.7848 8.5 21V54C8.5 58.2179 9.58979 61.5562 11.8359 63.9427Z"/><defs><linearGradient id="paint0_linear_498_146" x1="38.942" x2="38.942" y1="0" y2="75" gradientUnits="userSpaceOnUse"><stop stop-color="#D33075"/><stop offset="1" stop-color="#6157D8"/></linearGradient><linearGradient id="paint1_linear_498_146" x1="38.942" x2="38.942" y1="0" y2="75" gradientUnits="userSpaceOnUse"><stop stop-color="#D33075"/><stop offset="1" stop-color="#6157D8"/></linearGradient></defs></svg>
|
||||
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 3.2 KiB |
5
.github/logo.svg
vendored
@@ -1,4 +1 @@
|
||||
<svg width="78" height="75" viewBox="0 0 78 75" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M21.2 75C16.8 75 13 74.1667 9.8 72.5C6.66667 70.8333 4.23333 68.4333 2.5 65.3C0.833333 62.1 0 58.3333 0 54V21C0 16.6 0.833333 12.8333 2.5 9.7C4.23333 6.56666 6.66667 4.16666 9.8 2.5C13 0.833333 16.8 0 21.2 0C25.6 0 29.3667 0.833333 32.5 2.5C35.7 4.16666 38.1333 6.56666 39.8 9.7C41.5333 12.8333 42.4 16.5667 42.4 20.9V54C42.4 58.3333 41.5333 62.1 39.8 65.3C38.1333 68.4333 35.7 70.8333 32.5 72.5C29.3667 74.1667 25.6 75 21.2 75ZM21.2 66.9C25.1333 66.9 28.1333 65.8 30.2 63.6C32.3333 61.3333 33.4 58.1333 33.4 54V21C33.4 16.8667 32.3333 13.7 30.2 11.5C28.1333 9.23333 25.1333 8.1 21.2 8.1C17.3333 8.1 14.3333 9.23333 12.2 11.5C10.0667 13.7 9 16.8667 9 21V54C9 58.1333 10.0667 61.3333 12.2 63.6C14.3333 65.8 17.3333 66.9 21.2 66.9Z" fill="black"/>
|
||||
<path d="M56.4831 75C52.0831 75 48.2498 74.1667 44.9831 72.5C41.7831 70.8333 39.2831 68.4333 37.4831 65.3C35.7498 62.1 34.8831 58.3333 34.8831 54V21C34.8831 16.6 35.7498 12.8333 37.4831 9.7C39.2831 6.56666 41.7831 4.16666 44.9831 2.5C48.2498 0.833333 52.0831 0 56.4831 0C60.8831 0 64.6831 0.866665 67.8831 2.6C71.0831 4.26667 73.5498 6.66667 75.2831 9.8C77.0165 12.9333 77.8831 16.6667 77.8831 21H68.8831C68.8831 16.8667 67.7831 13.7 65.5831 11.5C63.4498 9.23333 60.4165 8.1 56.4831 8.1C52.5498 8.1 49.4498 9.2 47.1831 11.4C44.9831 13.6 43.8831 16.7667 43.8831 20.9V54C43.8831 58.1333 44.9831 61.3333 47.1831 63.6C49.4498 65.8 52.5498 66.9 56.4831 66.9C60.4165 66.9 63.4498 65.8 65.5831 63.6C67.7831 61.3333 68.8831 58.1333 68.8831 54H77.8831C77.8831 58.2667 77.0165 62 75.2831 65.2C73.5498 68.3333 71.0831 70.7667 67.8831 72.5C64.6831 74.1667 60.8831 75 56.4831 75Z" fill="black"/>
|
||||
</svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="78" height="75" fill="none" viewBox="0 0 78 75"><path fill="#000" d="M21.2 75C16.8 75 13 74.1667 9.8 72.5C6.66667 70.8333 4.23333 68.4333 2.5 65.3C0.833333 62.1 0 58.3333 0 54V21C0 16.6 0.833333 12.8333 2.5 9.7C4.23333 6.56666 6.66667 4.16666 9.8 2.5C13 0.833333 16.8 0 21.2 0C25.6 0 29.3667 0.833333 32.5 2.5C35.7 4.16666 38.1333 6.56666 39.8 9.7C41.5333 12.8333 42.4 16.5667 42.4 20.9V54C42.4 58.3333 41.5333 62.1 39.8 65.3C38.1333 68.4333 35.7 70.8333 32.5 72.5C29.3667 74.1667 25.6 75 21.2 75ZM21.2 66.9C25.1333 66.9 28.1333 65.8 30.2 63.6C32.3333 61.3333 33.4 58.1333 33.4 54V21C33.4 16.8667 32.3333 13.7 30.2 11.5C28.1333 9.23333 25.1333 8.1 21.2 8.1C17.3333 8.1 14.3333 9.23333 12.2 11.5C10.0667 13.7 9 16.8667 9 21V54C9 58.1333 10.0667 61.3333 12.2 63.6C14.3333 65.8 17.3333 66.9 21.2 66.9Z"/><path fill="#000" d="M56.4831 75C52.0831 75 48.2498 74.1667 44.9831 72.5C41.7831 70.8333 39.2831 68.4333 37.4831 65.3C35.7498 62.1 34.8831 58.3333 34.8831 54V21C34.8831 16.6 35.7498 12.8333 37.4831 9.7C39.2831 6.56666 41.7831 4.16666 44.9831 2.5C48.2498 0.833333 52.0831 0 56.4831 0C60.8831 0 64.6831 0.866665 67.8831 2.6C71.0831 4.26667 73.5498 6.66667 75.2831 9.8C77.0165 12.9333 77.8831 16.6667 77.8831 21H68.8831C68.8831 16.8667 67.7831 13.7 65.5831 11.5C63.4498 9.23333 60.4165 8.1 56.4831 8.1C52.5498 8.1 49.4498 9.2 47.1831 11.4C44.9831 13.6 43.8831 16.7667 43.8831 20.9V54C43.8831 58.1333 44.9831 61.3333 47.1831 63.6C49.4498 65.8 52.5498 66.9 56.4831 66.9C60.4165 66.9 63.4498 65.8 65.5831 63.6C67.7831 61.3333 68.8831 58.1333 68.8831 54H77.8831C77.8831 58.2667 77.0165 62 75.2831 65.2C73.5498 68.3333 71.0831 70.7667 67.8831 72.5C64.6831 74.1667 60.8831 75 56.4831 75Z"/></svg>
|
||||
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
BIN
.github/opencommit-example.png
vendored
|
Before Width: | Height: | Size: 304 KiB After Width: | Height: | Size: 237 KiB |
18
.github/workflows/test.yml
vendored
@@ -51,3 +51,21 @@ jobs:
|
||||
run: npm run build
|
||||
- name: Run E2E Tests
|
||||
run: npm run test:e2e
|
||||
prettier:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Use Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20.x'
|
||||
cache: 'npm'
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
- name: Run Prettier
|
||||
run: npm run format:check
|
||||
- name: Prettier Output
|
||||
if: failure()
|
||||
run: |
|
||||
echo "Prettier check failed. Please run 'npm run format' to fix formatting issues."
|
||||
exit 1
|
||||
|
||||
18
README.md
@@ -74,6 +74,22 @@ oco config set OCO_API_URL='http://192.168.1.10:11434/api/chat'
|
||||
|
||||
where 192.168.1.10 is example of endpoint URL, where you have ollama set up.
|
||||
|
||||
#### Troubleshooting Ollama IPv6/IPv4 Connection Fix
|
||||
|
||||
If you encounter issues with Ollama, such as the error
|
||||
|
||||
```sh
|
||||
✖ local model issues. details: connect ECONNREFUSED ::1:11434
|
||||
```
|
||||
|
||||
It's likely because Ollama is not listening on IPv6 by default. To fix this, you can set the OLLAMA_HOST environment variable to 0.0.0.0 before starting Ollama:
|
||||
|
||||
```bash
|
||||
export OLLAMA_HOST=0.0.0.0
|
||||
```
|
||||
|
||||
This will make Ollama listen on all interfaces, including IPv6 and IPv4, resolving the connection issue. You can add this line to your shell configuration file (like `.bashrc` or `.zshrc`) to make it persistent across sessions.
|
||||
|
||||
### Flags
|
||||
|
||||
There are multiple optional flags that can be used with the `oco` command:
|
||||
@@ -106,7 +122,7 @@ Create a `.env` file and add OpenCommit config variables there like this:
|
||||
|
||||
```env
|
||||
...
|
||||
OCO_AI_PROVIDER=<openai (default), anthropic, azure, ollama, gemini, flowise, deepseek>
|
||||
OCO_AI_PROVIDER=<openai (default), anthropic, azure, ollama, gemini, flowise, deepseek, aimlapi>
|
||||
OCO_API_KEY=<your OpenAI API token> // or other LLM provider API token
|
||||
OCO_API_URL=<may be used to set proxy path to OpenAI api>
|
||||
OCO_API_CUSTOM_HEADERS=<JSON string of custom HTTP headers to include in API requests>
|
||||
|
||||
1244
out/cli.cjs
@@ -70705,10 +70705,10 @@ var ja_default = {
|
||||
var ko_default = {
|
||||
localLanguage: "\uD55C\uAD6D\uC5B4",
|
||||
commitFix: "fix(server.ts): \uD3EC\uD2B8 \uBCC0\uC218\uB97C \uC18C\uBB38\uC790 port\uC5D0\uC11C \uB300\uBB38\uC790 PORT\uB85C \uBCC0\uACBD",
|
||||
commitFeat: "\uD53C\uD2B8(server.ts): process.env.PORT \uD658\uACBD \uBCC0\uC218 \uC9C0\uC6D0 \uCD94\uAC00",
|
||||
commitFeat: "feat(server.ts): process.env.PORT \uD658\uACBD \uBCC0\uC218 \uC9C0\uC6D0 \uCD94\uAC00",
|
||||
commitDescription: "\uD3EC\uD2B8 \uBCC0\uC218\uB294 \uC774\uC81C PORT\uB85C \uC774\uB984\uC774 \uC9C0\uC815\uB418\uC5B4 \uC0C1\uC218\uC778 PORT\uC640 \uC77C\uAD00\uC131 \uC788\uB294 \uC774\uB984 \uADDC\uCE59\uC744 \uB530\uB985\uB2C8\uB2E4. \uD658\uACBD \uBCC0\uC218 \uC9C0\uC6D0\uC744 \uD1B5\uD574 \uC560\uD50C\uB9AC\uCF00\uC774\uC158\uC740 \uC774\uC81C process.env.PORT \uD658\uACBD \uBCC0\uC218\uB85C \uC9C0\uC815\uB41C \uC0AC\uC6A9 \uAC00\uB2A5\uD55C \uBAA8\uB4E0 \uD3EC\uD2B8\uC5D0\uC11C \uC2E4\uD589\uD560 \uC218 \uC788\uC73C\uBBC0\uB85C \uB354 \uC720\uC5F0\uD574\uC84C\uC2B5\uB2C8\uB2E4.",
|
||||
commitFixOmitScope: "fix: \uD3EC\uD2B8 \uBCC0\uC218\uB97C \uC18C\uBB38\uC790 port\uC5D0\uC11C \uB300\uBB38\uC790 PORT\uB85C \uBCC0\uACBD",
|
||||
commitFeatOmitScope: "\uD53C\uD2B8: process.env.PORT \uD658\uACBD \uBCC0\uC218 \uC9C0\uC6D0 \uCD94\uAC00"
|
||||
commitFeatOmitScope: "feat: process.env.PORT \uD658\uACBD \uBCC0\uC218 \uC9C0\uC6D0 \uCD94\uAC00"
|
||||
};
|
||||
|
||||
// src/i18n/nl.json
|
||||
@@ -70883,6 +70883,7 @@ var CONFIG_KEYS = /* @__PURE__ */ ((CONFIG_KEYS2) => {
|
||||
CONFIG_KEYS2["OCO_API_CUSTOM_HEADERS"] = "OCO_API_CUSTOM_HEADERS";
|
||||
CONFIG_KEYS2["OCO_OMIT_SCOPE"] = "OCO_OMIT_SCOPE";
|
||||
CONFIG_KEYS2["OCO_GITPUSH"] = "OCO_GITPUSH";
|
||||
CONFIG_KEYS2["OCO_HOOK_AUTO_UNCOMMENT"] = "OCO_HOOK_AUTO_UNCOMMENT";
|
||||
return CONFIG_KEYS2;
|
||||
})(CONFIG_KEYS || {});
|
||||
var MODEL_LIST = {
|
||||
@@ -70984,7 +70985,442 @@ var MODEL_LIST = {
|
||||
"mistral-moderation-2411",
|
||||
"mistral-moderation-latest"
|
||||
],
|
||||
deepseek: ["deepseek-chat", "deepseek-reasoner"]
|
||||
deepseek: ["deepseek-chat", "deepseek-reasoner"],
|
||||
// AI/ML API available chat-completion models
|
||||
// https://api.aimlapi.com/v1/models
|
||||
aimlapi: [
|
||||
"openai/gpt-4o",
|
||||
"gpt-4o-2024-08-06",
|
||||
"gpt-4o-2024-05-13",
|
||||
"gpt-4o-mini",
|
||||
"gpt-4o-mini-2024-07-18",
|
||||
"chatgpt-4o-latest",
|
||||
"gpt-4-turbo",
|
||||
"gpt-4-turbo-2024-04-09",
|
||||
"gpt-4",
|
||||
"gpt-4-0125-preview",
|
||||
"gpt-4-1106-preview",
|
||||
"gpt-3.5-turbo",
|
||||
"gpt-3.5-turbo-0125",
|
||||
"gpt-3.5-turbo-1106",
|
||||
"o1-preview",
|
||||
"o1-preview-2024-09-12",
|
||||
"o1-mini",
|
||||
"o1-mini-2024-09-12",
|
||||
"o3-mini",
|
||||
"gpt-4o-audio-preview",
|
||||
"gpt-4o-mini-audio-preview",
|
||||
"gpt-4o-search-preview",
|
||||
"gpt-4o-mini-search-preview",
|
||||
"openai/gpt-4.1-2025-04-14",
|
||||
"openai/gpt-4.1-mini-2025-04-14",
|
||||
"openai/gpt-4.1-nano-2025-04-14",
|
||||
"openai/o4-mini-2025-04-16",
|
||||
"openai/o3-2025-04-16",
|
||||
"o1",
|
||||
"openai/o3-pro",
|
||||
"meta-llama/Llama-3.2-90B-Vision-Instruct-Turbo",
|
||||
"google/gemma-2-27b-it",
|
||||
"meta-llama/Llama-Vision-Free",
|
||||
"Qwen/Qwen2-72B-Instruct",
|
||||
"mistralai/Mixtral-8x7B-Instruct-v0.1",
|
||||
"nvidia/Llama-3.1-Nemotron-70B-Instruct-HF",
|
||||
"NousResearch/Nous-Hermes-2-Mixtral-8x7B-DPO",
|
||||
"meta-llama/Llama-3.3-70B-Instruct-Turbo",
|
||||
"meta-llama/Llama-3.2-3B-Instruct-Turbo",
|
||||
"meta-llama/Llama-3.2-11B-Vision-Instruct-Turbo",
|
||||
"meta-llama/Llama-Guard-3-11B-Vision-Turbo",
|
||||
"Qwen/Qwen2.5-7B-Instruct-Turbo",
|
||||
"Qwen/Qwen2.5-Coder-32B-Instruct",
|
||||
"meta-llama/Meta-Llama-3-8B-Instruct-Lite",
|
||||
"meta-llama/Llama-3-8b-chat-hf",
|
||||
"meta-llama/Llama-3-70b-chat-hf",
|
||||
"Qwen/Qwen2.5-72B-Instruct-Turbo",
|
||||
"Qwen/QwQ-32B",
|
||||
"meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo",
|
||||
"meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo",
|
||||
"meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo",
|
||||
"mistralai/Mistral-7B-Instruct-v0.2",
|
||||
"meta-llama/LlamaGuard-2-8b",
|
||||
"mistralai/Mistral-7B-Instruct-v0.1",
|
||||
"mistralai/Mistral-7B-Instruct-v0.3",
|
||||
"meta-llama/Meta-Llama-Guard-3-8B",
|
||||
"meta-llama/llama-4-scout",
|
||||
"meta-llama/llama-4-maverick",
|
||||
"Qwen/Qwen3-235B-A22B-fp8-tput",
|
||||
"claude-3-opus-20240229",
|
||||
"claude-3-haiku-20240307",
|
||||
"claude-3-5-sonnet-20240620",
|
||||
"claude-3-5-sonnet-20241022",
|
||||
"claude-3-5-haiku-20241022",
|
||||
"claude-3-7-sonnet-20250219",
|
||||
"claude-sonnet-4-20250514",
|
||||
"claude-opus-4-20250514",
|
||||
"google/gemini-2.0-flash-exp",
|
||||
"google/gemini-2.0-flash",
|
||||
"google/gemini-2.5-pro",
|
||||
"google/gemini-2.5-flash",
|
||||
"deepseek-chat",
|
||||
"deepseek-reasoner",
|
||||
"qwen-max",
|
||||
"qwen-plus",
|
||||
"qwen-turbo",
|
||||
"qwen-max-2025-01-25",
|
||||
"mistralai/mistral-tiny",
|
||||
"mistralai/mistral-nemo",
|
||||
"anthracite-org/magnum-v4-72b",
|
||||
"nvidia/llama-3.1-nemotron-70b-instruct",
|
||||
"cohere/command-r-plus",
|
||||
"mistralai/codestral-2501",
|
||||
"google/gemma-3-4b-it",
|
||||
"google/gemma-3-12b-it",
|
||||
"google/gemma-3-27b-it",
|
||||
"google/gemini-2.5-flash-lite-preview",
|
||||
"deepseek/deepseek-prover-v2",
|
||||
"google/gemma-3n-e4b-it",
|
||||
"cohere/command-a",
|
||||
"MiniMax-Text-01",
|
||||
"abab6.5s-chat",
|
||||
"minimax/m1",
|
||||
"bagoodex/bagoodex-search-v1",
|
||||
"moonshot/kimi-k2-preview",
|
||||
"perplexity/sonar",
|
||||
"perplexity/sonar-pro",
|
||||
"x-ai/grok-4-07-09",
|
||||
"x-ai/grok-3-beta",
|
||||
"x-ai/grok-3-mini-beta"
|
||||
],
|
||||
// OpenRouter available models
|
||||
// input_modalities: 'text'
|
||||
// output_modalities: 'text'
|
||||
// https://openrouter.ai/api/v1/models
|
||||
openrouter: [
|
||||
"openai/gpt-4o-mini",
|
||||
// used by default
|
||||
"01-ai/yi-large",
|
||||
"aetherwiing/mn-starcannon-12b",
|
||||
"agentica-org/deepcoder-14b-preview:free",
|
||||
"ai21/jamba-1.6-large",
|
||||
"ai21/jamba-1.6-mini",
|
||||
"aion-labs/aion-1.0",
|
||||
"aion-labs/aion-1.0-mini",
|
||||
"aion-labs/aion-rp-llama-3.1-8b",
|
||||
"alfredpros/codellama-7b-instruct-solidity",
|
||||
"all-hands/openhands-lm-32b-v0.1",
|
||||
"alpindale/goliath-120b",
|
||||
"alpindale/magnum-72b",
|
||||
"amazon/nova-lite-v1",
|
||||
"amazon/nova-micro-v1",
|
||||
"amazon/nova-pro-v1",
|
||||
"anthracite-org/magnum-v2-72b",
|
||||
"anthracite-org/magnum-v4-72b",
|
||||
"anthropic/claude-2",
|
||||
"anthropic/claude-2.0",
|
||||
"anthropic/claude-2.0:beta",
|
||||
"anthropic/claude-2.1",
|
||||
"anthropic/claude-2.1:beta",
|
||||
"anthropic/claude-2:beta",
|
||||
"anthropic/claude-3-haiku",
|
||||
"anthropic/claude-3-haiku:beta",
|
||||
"anthropic/claude-3-opus",
|
||||
"anthropic/claude-3-opus:beta",
|
||||
"anthropic/claude-3-sonnet",
|
||||
"anthropic/claude-3-sonnet:beta",
|
||||
"anthropic/claude-3.5-haiku",
|
||||
"anthropic/claude-3.5-haiku-20241022",
|
||||
"anthropic/claude-3.5-haiku-20241022:beta",
|
||||
"anthropic/claude-3.5-haiku:beta",
|
||||
"anthropic/claude-3.5-sonnet",
|
||||
"anthropic/claude-3.5-sonnet-20240620",
|
||||
"anthropic/claude-3.5-sonnet-20240620:beta",
|
||||
"anthropic/claude-3.5-sonnet:beta",
|
||||
"anthropic/claude-3.7-sonnet",
|
||||
"anthropic/claude-3.7-sonnet:beta",
|
||||
"anthropic/claude-3.7-sonnet:thinking",
|
||||
"anthropic/claude-opus-4",
|
||||
"anthropic/claude-sonnet-4",
|
||||
"arcee-ai/arcee-blitz",
|
||||
"arcee-ai/caller-large",
|
||||
"arcee-ai/coder-large",
|
||||
"arcee-ai/maestro-reasoning",
|
||||
"arcee-ai/spotlight",
|
||||
"arcee-ai/virtuoso-large",
|
||||
"arcee-ai/virtuoso-medium-v2",
|
||||
"arliai/qwq-32b-arliai-rpr-v1:free",
|
||||
"cognitivecomputations/dolphin-mixtral-8x22b",
|
||||
"cognitivecomputations/dolphin3.0-mistral-24b:free",
|
||||
"cognitivecomputations/dolphin3.0-r1-mistral-24b:free",
|
||||
"cohere/command",
|
||||
"cohere/command-a",
|
||||
"cohere/command-r",
|
||||
"cohere/command-r-03-2024",
|
||||
"cohere/command-r-08-2024",
|
||||
"cohere/command-r-plus",
|
||||
"cohere/command-r-plus-04-2024",
|
||||
"cohere/command-r-plus-08-2024",
|
||||
"cohere/command-r7b-12-2024",
|
||||
"deepseek/deepseek-chat",
|
||||
"deepseek/deepseek-chat-v3-0324",
|
||||
"deepseek/deepseek-chat-v3-0324:free",
|
||||
"deepseek/deepseek-chat:free",
|
||||
"deepseek/deepseek-prover-v2",
|
||||
"deepseek/deepseek-prover-v2:free",
|
||||
"deepseek/deepseek-r1",
|
||||
"deepseek/deepseek-r1-0528",
|
||||
"deepseek/deepseek-r1-0528-qwen3-8b",
|
||||
"deepseek/deepseek-r1-0528-qwen3-8b:free",
|
||||
"deepseek/deepseek-r1-0528:free",
|
||||
"deepseek/deepseek-r1-distill-llama-70b",
|
||||
"deepseek/deepseek-r1-distill-llama-70b:free",
|
||||
"deepseek/deepseek-r1-distill-llama-8b",
|
||||
"deepseek/deepseek-r1-distill-qwen-1.5b",
|
||||
"deepseek/deepseek-r1-distill-qwen-14b",
|
||||
"deepseek/deepseek-r1-distill-qwen-14b:free",
|
||||
"deepseek/deepseek-r1-distill-qwen-32b",
|
||||
"deepseek/deepseek-r1-distill-qwen-32b:free",
|
||||
"deepseek/deepseek-r1-distill-qwen-7b",
|
||||
"deepseek/deepseek-r1-zero:free",
|
||||
"deepseek/deepseek-r1:free",
|
||||
"deepseek/deepseek-v3-base:free",
|
||||
"eleutherai/llemma_7b",
|
||||
"eva-unit-01/eva-llama-3.33-70b",
|
||||
"eva-unit-01/eva-qwen-2.5-32b",
|
||||
"eva-unit-01/eva-qwen-2.5-72b",
|
||||
"featherless/qwerky-72b:free",
|
||||
"google/gemini-2.0-flash-001",
|
||||
"google/gemini-2.0-flash-exp:free",
|
||||
"google/gemini-2.0-flash-lite-001",
|
||||
"google/gemini-2.5-flash-preview",
|
||||
"google/gemini-2.5-flash-preview-05-20",
|
||||
"google/gemini-2.5-flash-preview-05-20:thinking",
|
||||
"google/gemini-2.5-flash-preview:thinking",
|
||||
"google/gemini-2.5-pro-exp-03-25",
|
||||
"google/gemini-2.5-pro-preview",
|
||||
"google/gemini-2.5-pro-preview-05-06",
|
||||
"google/gemini-flash-1.5",
|
||||
"google/gemini-flash-1.5-8b",
|
||||
"google/gemini-pro-1.5",
|
||||
"google/gemma-2-27b-it",
|
||||
"google/gemma-2-9b-it",
|
||||
"google/gemma-2-9b-it:free",
|
||||
"google/gemma-3-12b-it",
|
||||
"google/gemma-3-12b-it:free",
|
||||
"google/gemma-3-1b-it:free",
|
||||
"google/gemma-3-27b-it",
|
||||
"google/gemma-3-27b-it:free",
|
||||
"google/gemma-3-4b-it",
|
||||
"google/gemma-3-4b-it:free",
|
||||
"google/gemma-3n-e4b-it:free",
|
||||
"gryphe/mythomax-l2-13b",
|
||||
"inception/mercury-coder-small-beta",
|
||||
"infermatic/mn-inferor-12b",
|
||||
"inflection/inflection-3-pi",
|
||||
"inflection/inflection-3-productivity",
|
||||
"liquid/lfm-3b",
|
||||
"liquid/lfm-40b",
|
||||
"liquid/lfm-7b",
|
||||
"mancer/weaver",
|
||||
"meta-llama/llama-2-70b-chat",
|
||||
"meta-llama/llama-3-70b-instruct",
|
||||
"meta-llama/llama-3-8b-instruct",
|
||||
"meta-llama/llama-3.1-405b",
|
||||
"meta-llama/llama-3.1-405b-instruct",
|
||||
"meta-llama/llama-3.1-405b:free",
|
||||
"meta-llama/llama-3.1-70b-instruct",
|
||||
"meta-llama/llama-3.1-8b-instruct",
|
||||
"meta-llama/llama-3.1-8b-instruct:free",
|
||||
"meta-llama/llama-3.2-11b-vision-instruct",
|
||||
"meta-llama/llama-3.2-11b-vision-instruct:free",
|
||||
"meta-llama/llama-3.2-1b-instruct",
|
||||
"meta-llama/llama-3.2-1b-instruct:free",
|
||||
"meta-llama/llama-3.2-3b-instruct",
|
||||
"meta-llama/llama-3.2-3b-instruct:free",
|
||||
"meta-llama/llama-3.2-90b-vision-instruct",
|
||||
"meta-llama/llama-3.3-70b-instruct",
|
||||
"meta-llama/llama-3.3-70b-instruct:free",
|
||||
"meta-llama/llama-3.3-8b-instruct:free",
|
||||
"meta-llama/llama-4-maverick",
|
||||
"meta-llama/llama-4-maverick:free",
|
||||
"meta-llama/llama-4-scout",
|
||||
"meta-llama/llama-4-scout:free",
|
||||
"meta-llama/llama-guard-2-8b",
|
||||
"meta-llama/llama-guard-3-8b",
|
||||
"meta-llama/llama-guard-4-12b",
|
||||
"microsoft/mai-ds-r1:free",
|
||||
"microsoft/phi-3-medium-128k-instruct",
|
||||
"microsoft/phi-3-mini-128k-instruct",
|
||||
"microsoft/phi-3.5-mini-128k-instruct",
|
||||
"microsoft/phi-4",
|
||||
"microsoft/phi-4-multimodal-instruct",
|
||||
"microsoft/phi-4-reasoning-plus",
|
||||
"microsoft/phi-4-reasoning-plus:free",
|
||||
"microsoft/phi-4-reasoning:free",
|
||||
"microsoft/wizardlm-2-8x22b",
|
||||
"minimax/minimax-01",
|
||||
"mistralai/codestral-2501",
|
||||
"mistralai/devstral-small",
|
||||
"mistralai/devstral-small:free",
|
||||
"mistralai/magistral-medium-2506",
|
||||
"mistralai/magistral-medium-2506:thinking",
|
||||
"mistralai/magistral-small-2506",
|
||||
"mistralai/ministral-3b",
|
||||
"mistralai/ministral-8b",
|
||||
"mistralai/mistral-7b-instruct",
|
||||
"mistralai/mistral-7b-instruct-v0.1",
|
||||
"mistralai/mistral-7b-instruct-v0.2",
|
||||
"mistralai/mistral-7b-instruct-v0.3",
|
||||
"mistralai/mistral-7b-instruct:free",
|
||||
"mistralai/mistral-large",
|
||||
"mistralai/mistral-large-2407",
|
||||
"mistralai/mistral-large-2411",
|
||||
"mistralai/mistral-medium",
|
||||
"mistralai/mistral-medium-3",
|
||||
"mistralai/mistral-nemo",
|
||||
"mistralai/mistral-nemo:free",
|
||||
"mistralai/mistral-saba",
|
||||
"mistralai/mistral-small",
|
||||
"mistralai/mistral-small-24b-instruct-2501",
|
||||
"mistralai/mistral-small-24b-instruct-2501:free",
|
||||
"mistralai/mistral-small-3.1-24b-instruct",
|
||||
"mistralai/mistral-small-3.1-24b-instruct:free",
|
||||
"mistralai/mistral-tiny",
|
||||
"mistralai/mixtral-8x22b-instruct",
|
||||
"mistralai/mixtral-8x7b-instruct",
|
||||
"mistralai/pixtral-12b",
|
||||
"mistralai/pixtral-large-2411",
|
||||
"moonshotai/kimi-vl-a3b-thinking:free",
|
||||
"moonshotai/moonlight-16b-a3b-instruct:free",
|
||||
"neversleep/llama-3-lumimaid-70b",
|
||||
"neversleep/llama-3-lumimaid-8b",
|
||||
"neversleep/llama-3.1-lumimaid-70b",
|
||||
"neversleep/llama-3.1-lumimaid-8b",
|
||||
"neversleep/noromaid-20b",
|
||||
"nothingiisreal/mn-celeste-12b",
|
||||
"nousresearch/deephermes-3-llama-3-8b-preview:free",
|
||||
"nousresearch/deephermes-3-mistral-24b-preview:free",
|
||||
"nousresearch/hermes-2-pro-llama-3-8b",
|
||||
"nousresearch/hermes-3-llama-3.1-405b",
|
||||
"nousresearch/hermes-3-llama-3.1-70b",
|
||||
"nousresearch/nous-hermes-2-mixtral-8x7b-dpo",
|
||||
"nvidia/llama-3.1-nemotron-70b-instruct",
|
||||
"nvidia/llama-3.1-nemotron-ultra-253b-v1",
|
||||
"nvidia/llama-3.1-nemotron-ultra-253b-v1:free",
|
||||
"nvidia/llama-3.3-nemotron-super-49b-v1",
|
||||
"nvidia/llama-3.3-nemotron-super-49b-v1:free",
|
||||
"open-r1/olympiccoder-32b:free",
|
||||
"openai/chatgpt-4o-latest",
|
||||
"openai/codex-mini",
|
||||
"openai/gpt-3.5-turbo",
|
||||
"openai/gpt-3.5-turbo-0125",
|
||||
"openai/gpt-3.5-turbo-0613",
|
||||
"openai/gpt-3.5-turbo-1106",
|
||||
"openai/gpt-3.5-turbo-16k",
|
||||
"openai/gpt-3.5-turbo-instruct",
|
||||
"openai/gpt-4",
|
||||
"openai/gpt-4-0314",
|
||||
"openai/gpt-4-1106-preview",
|
||||
"openai/gpt-4-turbo",
|
||||
"openai/gpt-4-turbo-preview",
|
||||
"openai/gpt-4.1",
|
||||
"openai/gpt-4.1-mini",
|
||||
"openai/gpt-4.1-nano",
|
||||
"openai/gpt-4.5-preview",
|
||||
"openai/gpt-4o",
|
||||
"openai/gpt-4o-2024-05-13",
|
||||
"openai/gpt-4o-2024-08-06",
|
||||
"openai/gpt-4o-2024-11-20",
|
||||
"openai/gpt-4o-mini-2024-07-18",
|
||||
"openai/gpt-4o-mini-search-preview",
|
||||
"openai/gpt-4o-search-preview",
|
||||
"openai/gpt-4o:extended",
|
||||
"openai/o1",
|
||||
"openai/o1-mini",
|
||||
"openai/o1-mini-2024-09-12",
|
||||
"openai/o1-preview",
|
||||
"openai/o1-preview-2024-09-12",
|
||||
"openai/o1-pro",
|
||||
"openai/o3",
|
||||
"openai/o3-mini",
|
||||
"openai/o3-mini-high",
|
||||
"openai/o3-pro",
|
||||
"openai/o4-mini",
|
||||
"openai/o4-mini-high",
|
||||
"opengvlab/internvl3-14b:free",
|
||||
"opengvlab/internvl3-2b:free",
|
||||
"openrouter/auto",
|
||||
"perplexity/llama-3.1-sonar-large-128k-online",
|
||||
"perplexity/llama-3.1-sonar-small-128k-online",
|
||||
"perplexity/r1-1776",
|
||||
"perplexity/sonar",
|
||||
"perplexity/sonar-deep-research",
|
||||
"perplexity/sonar-pro",
|
||||
"perplexity/sonar-reasoning",
|
||||
"perplexity/sonar-reasoning-pro",
|
||||
"pygmalionai/mythalion-13b",
|
||||
"qwen/qwen-2-72b-instruct",
|
||||
"qwen/qwen-2.5-72b-instruct",
|
||||
"qwen/qwen-2.5-72b-instruct:free",
|
||||
"qwen/qwen-2.5-7b-instruct",
|
||||
"qwen/qwen-2.5-7b-instruct:free",
|
||||
"qwen/qwen-2.5-coder-32b-instruct",
|
||||
"qwen/qwen-2.5-coder-32b-instruct:free",
|
||||
"qwen/qwen-2.5-vl-7b-instruct",
|
||||
"qwen/qwen-2.5-vl-7b-instruct:free",
|
||||
"qwen/qwen-max",
|
||||
"qwen/qwen-plus",
|
||||
"qwen/qwen-turbo",
|
||||
"qwen/qwen-vl-max",
|
||||
"qwen/qwen-vl-plus",
|
||||
"qwen/qwen2.5-vl-32b-instruct",
|
||||
"qwen/qwen2.5-vl-32b-instruct:free",
|
||||
"qwen/qwen2.5-vl-3b-instruct:free",
|
||||
"qwen/qwen2.5-vl-72b-instruct",
|
||||
"qwen/qwen2.5-vl-72b-instruct:free",
|
||||
"qwen/qwen3-14b",
|
||||
"qwen/qwen3-14b:free",
|
||||
"qwen/qwen3-235b-a22b",
|
||||
"qwen/qwen3-235b-a22b:free",
|
||||
"qwen/qwen3-30b-a3b",
|
||||
"qwen/qwen3-30b-a3b:free",
|
||||
"qwen/qwen3-32b",
|
||||
"qwen/qwen3-32b:free",
|
||||
"qwen/qwen3-8b",
|
||||
"qwen/qwen3-8b:free",
|
||||
"qwen/qwq-32b",
|
||||
"qwen/qwq-32b-preview",
|
||||
"qwen/qwq-32b:free",
|
||||
"raifle/sorcererlm-8x22b",
|
||||
"rekaai/reka-flash-3:free",
|
||||
"sao10k/fimbulvetr-11b-v2",
|
||||
"sao10k/l3-euryale-70b",
|
||||
"sao10k/l3-lunaris-8b",
|
||||
"sao10k/l3.1-euryale-70b",
|
||||
"sao10k/l3.3-euryale-70b",
|
||||
"sarvamai/sarvam-m:free",
|
||||
"scb10x/llama3.1-typhoon2-70b-instruct",
|
||||
"sentientagi/dobby-mini-unhinged-plus-llama-3.1-8b",
|
||||
"shisa-ai/shisa-v2-llama3.3-70b:free",
|
||||
"sophosympatheia/midnight-rose-70b",
|
||||
"thedrummer/anubis-pro-105b-v1",
|
||||
"thedrummer/rocinante-12b",
|
||||
"thedrummer/skyfall-36b-v2",
|
||||
"thedrummer/unslopnemo-12b",
|
||||
"thedrummer/valkyrie-49b-v1",
|
||||
"thudm/glm-4-32b",
|
||||
"thudm/glm-4-32b:free",
|
||||
"thudm/glm-z1-32b",
|
||||
"thudm/glm-z1-32b:free",
|
||||
"thudm/glm-z1-rumination-32b",
|
||||
"tngtech/deepseek-r1t-chimera:free",
|
||||
"undi95/remm-slerp-l2-13b",
|
||||
"undi95/toppy-m-7b",
|
||||
"x-ai/grok-2-1212",
|
||||
"x-ai/grok-2-vision-1212",
|
||||
"x-ai/grok-3-beta",
|
||||
"x-ai/grok-3-mini-beta",
|
||||
"x-ai/grok-beta",
|
||||
"x-ai/grok-vision-beta"
|
||||
]
|
||||
};
|
||||
var getDefaultModel = (provider) => {
|
||||
switch (provider) {
|
||||
@@ -71002,6 +71438,10 @@ var getDefaultModel = (provider) => {
|
||||
return MODEL_LIST.mistral[0];
|
||||
case "deepseek":
|
||||
return MODEL_LIST.deepseek[0];
|
||||
case "aimlapi":
|
||||
return MODEL_LIST.aimlapi[0];
|
||||
case "openrouter":
|
||||
return MODEL_LIST.openrouter[0];
|
||||
default:
|
||||
return MODEL_LIST.openai[0];
|
||||
}
|
||||
@@ -71155,9 +71595,11 @@ var configValidators = {
|
||||
"test",
|
||||
"flowise",
|
||||
"groq",
|
||||
"deepseek"
|
||||
"deepseek",
|
||||
"aimlapi",
|
||||
"openrouter"
|
||||
].includes(value) || value.startsWith("ollama"),
|
||||
`${value} is not supported yet, use 'ollama', 'mlx', 'anthropic', 'azure', 'gemini', 'flowise', 'mistral', 'deepseek' or 'openai' (default)`
|
||||
`${value} is not supported yet, use 'ollama', 'mlx', 'anthropic', 'azure', 'gemini', 'flowise', 'mistral', 'deepseek', 'aimlapi' or 'openai' (default)`
|
||||
);
|
||||
return value;
|
||||
},
|
||||
@@ -71186,6 +71628,13 @@ var configValidators = {
|
||||
"Must be true or false"
|
||||
);
|
||||
return value;
|
||||
},
|
||||
["OCO_HOOK_AUTO_UNCOMMENT" /* OCO_HOOK_AUTO_UNCOMMENT */](value) {
|
||||
validateConfig(
|
||||
"OCO_HOOK_AUTO_UNCOMMENT" /* OCO_HOOK_AUTO_UNCOMMENT */,
|
||||
typeof value === "boolean",
|
||||
"Must be true or false"
|
||||
);
|
||||
}
|
||||
};
|
||||
var OCO_AI_PROVIDER_ENUM = /* @__PURE__ */ ((OCO_AI_PROVIDER_ENUM2) => {
|
||||
@@ -71200,6 +71649,8 @@ var OCO_AI_PROVIDER_ENUM = /* @__PURE__ */ ((OCO_AI_PROVIDER_ENUM2) => {
|
||||
OCO_AI_PROVIDER_ENUM2["MISTRAL"] = "mistral";
|
||||
OCO_AI_PROVIDER_ENUM2["MLX"] = "mlx";
|
||||
OCO_AI_PROVIDER_ENUM2["DEEPSEEK"] = "deepseek";
|
||||
OCO_AI_PROVIDER_ENUM2["AIMLAPI"] = "aimlapi";
|
||||
OCO_AI_PROVIDER_ENUM2["OPENROUTER"] = "openrouter";
|
||||
return OCO_AI_PROVIDER_ENUM2;
|
||||
})(OCO_AI_PROVIDER_ENUM || {});
|
||||
var defaultConfigPath = (0, import_path.join)((0, import_os.homedir)(), ".opencommit");
|
||||
@@ -71223,8 +71674,9 @@ var DEFAULT_CONFIG = {
|
||||
OCO_TEST_MOCK_TYPE: "commit-message",
|
||||
OCO_WHY: false,
|
||||
OCO_OMIT_SCOPE: false,
|
||||
OCO_GITPUSH: true
|
||||
OCO_GITPUSH: true,
|
||||
// todo: deprecate
|
||||
OCO_HOOK_AUTO_UNCOMMENT: false
|
||||
};
|
||||
var initGlobalConfig = (configPath = defaultConfigPath) => {
|
||||
(0, import_fs.writeFileSync)(configPath, (0, import_ini.stringify)(DEFAULT_CONFIG), "utf8");
|
||||
@@ -71425,6 +71877,11 @@ function getConfigKeyDetails(key) {
|
||||
description: "Message template placeholder",
|
||||
values: ["String (must start with $)"]
|
||||
};
|
||||
case "OCO_HOOK_AUTO_UNCOMMENT" /* OCO_HOOK_AUTO_UNCOMMENT */:
|
||||
return {
|
||||
description: "Automatically uncomment the commit message in the hook",
|
||||
values: ["true", "false"]
|
||||
};
|
||||
default:
|
||||
return {
|
||||
description: "String value",
|
||||
@@ -71487,7 +71944,11 @@ ${key}:`));
|
||||
}
|
||||
}
|
||||
}
|
||||
console.log(source_default.yellow('\nUse "oco config describe [PARAMETER]" to see accepted values and more details for a specific config parameter.'));
|
||||
console.log(
|
||||
source_default.yellow(
|
||||
'\nUse "oco config describe [PARAMETER]" to see accepted values and more details for a specific config parameter.'
|
||||
)
|
||||
);
|
||||
}
|
||||
var configCommand = G2(
|
||||
{
|
||||
@@ -86693,8 +87154,7 @@ var MistralAiEngine = class {
|
||||
if (REQUEST_TOKENS > this.config.maxTokensInput - this.config.maxTokensOutput)
|
||||
throw new Error("TOO_MUCH_TOKENS" /* tooMuchTokens */);
|
||||
const completion = await this.client.chat.complete(params);
|
||||
if (!completion.choices)
|
||||
throw Error("No completion choice available.");
|
||||
if (!completion.choices) throw Error("No completion choice available.");
|
||||
const message = completion.choices[0].message;
|
||||
if (!message || !message.content)
|
||||
throw Error("No completion choice available.");
|
||||
@@ -86713,7 +87173,10 @@ var MistralAiEngine = class {
|
||||
if (!config6.baseURL) {
|
||||
this.client = new Mistral({ apiKey: config6.apiKey });
|
||||
} else {
|
||||
this.client = new Mistral({ apiKey: config6.apiKey, serverURL: config6.baseURL });
|
||||
this.client = new Mistral({
|
||||
apiKey: config6.apiKey,
|
||||
serverURL: config6.baseURL
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -86795,6 +87258,74 @@ var DeepseekEngine = class extends OpenAiEngine {
|
||||
}
|
||||
};
|
||||
|
||||
// src/engine/aimlapi.ts
|
||||
var AimlApiEngine = class {
|
||||
constructor(config6) {
|
||||
this.config = config6;
|
||||
this.generateCommitMessage = async (messages) => {
|
||||
try {
|
||||
const response = await this.client.post("", {
|
||||
model: this.config.model,
|
||||
messages
|
||||
});
|
||||
const message = response.data.choices?.[0]?.message;
|
||||
return message?.content ?? null;
|
||||
} catch (error) {
|
||||
const err = error;
|
||||
if (axios_default.isAxiosError(error) && error.response?.status === 401) {
|
||||
const apiError = error.response.data.error;
|
||||
if (apiError) throw new Error(apiError.message);
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
this.client = axios_default.create({
|
||||
baseURL: config6.baseURL || "https://api.aimlapi.com/v1/chat/completions",
|
||||
headers: {
|
||||
Authorization: `Bearer ${config6.apiKey}`,
|
||||
"HTTP-Referer": "https://github.com/di-sukharev/opencommit",
|
||||
"X-Title": "opencommit",
|
||||
"Content-Type": "application/json",
|
||||
...config6.customHeaders
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// src/engine/openrouter.ts
|
||||
var OpenRouterEngine = class {
|
||||
constructor(config6) {
|
||||
this.config = config6;
|
||||
this.generateCommitMessage = async (messages) => {
|
||||
try {
|
||||
const response = await this.client.post("", {
|
||||
model: this.config.model,
|
||||
messages
|
||||
});
|
||||
const message = response.data.choices[0].message;
|
||||
let content = message?.content;
|
||||
return removeContentTags(content, "think");
|
||||
} catch (error) {
|
||||
const err = error;
|
||||
if (axios_default.isAxiosError(error) && error.response?.status === 401) {
|
||||
const openRouterError = error.response.data.error;
|
||||
if (openRouterError) throw new Error(openRouterError.message);
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
this.client = axios_default.create({
|
||||
baseURL: "https://openrouter.ai/api/v1/chat/completions",
|
||||
headers: {
|
||||
Authorization: `Bearer ${config6.apiKey}`,
|
||||
"HTTP-Referer": "https://github.com/di-sukharev/opencommit",
|
||||
"X-Title": "OpenCommit",
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// src/utils/engine.ts
|
||||
function parseCustomHeaders(headers) {
|
||||
let parsedHeaders = {};
|
||||
@@ -86808,7 +87339,9 @@ function parseCustomHeaders(headers) {
|
||||
parsedHeaders = JSON.parse(headers);
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn("Invalid OCO_API_CUSTOM_HEADERS format, ignoring custom headers");
|
||||
console.warn(
|
||||
"Invalid OCO_API_CUSTOM_HEADERS format, ignoring custom headers"
|
||||
);
|
||||
}
|
||||
return parsedHeaders;
|
||||
}
|
||||
@@ -86845,6 +87378,10 @@ function getEngine() {
|
||||
return new MLXEngine(DEFAULT_CONFIG2);
|
||||
case "deepseek" /* DEEPSEEK */:
|
||||
return new DeepseekEngine(DEFAULT_CONFIG2);
|
||||
case "aimlapi" /* AIMLAPI */:
|
||||
return new AimlApiEngine(DEFAULT_CONFIG2);
|
||||
case "openrouter" /* OPENROUTER */:
|
||||
return new OpenRouterEngine(DEFAULT_CONFIG2);
|
||||
default:
|
||||
return new OpenAiEngine(DEFAULT_CONFIG2);
|
||||
}
|
||||
@@ -87356,7 +87893,10 @@ var config5 = getConfig();
|
||||
var MAX_TOKENS_INPUT = config5.OCO_TOKENS_MAX_INPUT;
|
||||
var MAX_TOKENS_OUTPUT = config5.OCO_TOKENS_MAX_OUTPUT;
|
||||
var generateCommitMessageChatCompletionPrompt = async (diff, fullGitMojiSpec, context2) => {
|
||||
const INIT_MESSAGES_PROMPT = await getMainCommitPrompt(fullGitMojiSpec, context2);
|
||||
const INIT_MESSAGES_PROMPT = await getMainCommitPrompt(
|
||||
fullGitMojiSpec,
|
||||
context2
|
||||
);
|
||||
const chatContextAsCompletionRequest = [...INIT_MESSAGES_PROMPT];
|
||||
chatContextAsCompletionRequest.push({
|
||||
role: "user",
|
||||
|
||||
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "opencommit",
|
||||
"version": "3.2.9",
|
||||
"version": "3.2.10",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "opencommit",
|
||||
"version": "3.2.9",
|
||||
"version": "3.2.10",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@actions/core": "^1.10.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "opencommit",
|
||||
"version": "3.2.9",
|
||||
"version": "3.2.10",
|
||||
"description": "Auto-generate impressive commits in 1 second. Killing lame commits with AI 🤯🔫",
|
||||
"keywords": [
|
||||
"git",
|
||||
@@ -51,6 +51,7 @@
|
||||
"deploy:patch": "npm version patch && npm run deploy:build",
|
||||
"lint": "eslint src --ext ts && tsc --noEmit",
|
||||
"format": "prettier --write src",
|
||||
"format:check": "prettier --check src",
|
||||
"test": "node --no-warnings --experimental-vm-modules $( [ -f ./node_modules/.bin/jest ] && echo ./node_modules/.bin/jest || which jest ) test/unit",
|
||||
"test:all": "npm run test:unit:docker && npm run test:e2e:docker",
|
||||
"test:docker-build": "docker build -t oco-test -f test/Dockerfile .",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import {
|
||||
text,
|
||||
confirm,
|
||||
intro,
|
||||
isCancel,
|
||||
@@ -85,15 +86,29 @@ ${commitMessage}
|
||||
${chalk.grey('——————————————————')}`
|
||||
);
|
||||
|
||||
const isCommitConfirmedByUser =
|
||||
skipCommitConfirmation ||
|
||||
(await confirm({
|
||||
message: 'Confirm the commit message?'
|
||||
}));
|
||||
const userAction = skipCommitConfirmation
|
||||
? 'Yes'
|
||||
: await select({
|
||||
message: 'Confirm the commit message?',
|
||||
options: [
|
||||
{ value: 'Yes', label: 'Yes' },
|
||||
{ value: 'No', label: 'No' },
|
||||
{ value: 'Edit', label: 'Edit' }
|
||||
]
|
||||
});
|
||||
|
||||
if (isCancel(isCommitConfirmedByUser)) process.exit(1);
|
||||
if (isCancel(userAction)) process.exit(1);
|
||||
|
||||
if (isCommitConfirmedByUser) {
|
||||
if (userAction === 'Edit') {
|
||||
const textResponse = await text({
|
||||
message: 'Please edit the commit message: (press Enter to continue)',
|
||||
initialValue: commitMessage
|
||||
});
|
||||
|
||||
commitMessage = textResponse.toString();
|
||||
}
|
||||
|
||||
if (userAction === 'Yes' || userAction === 'Edit') {
|
||||
const committingChangesSpinner = spinner();
|
||||
committingChangesSpinner.start('Committing the changes');
|
||||
const { stdout } = await execa('git', [
|
||||
|
||||
@@ -27,7 +27,8 @@ export enum CONFIG_KEYS {
|
||||
OCO_API_URL = 'OCO_API_URL',
|
||||
OCO_API_CUSTOM_HEADERS = 'OCO_API_CUSTOM_HEADERS',
|
||||
OCO_OMIT_SCOPE = 'OCO_OMIT_SCOPE',
|
||||
OCO_GITPUSH = 'OCO_GITPUSH' // todo: deprecate
|
||||
OCO_GITPUSH = 'OCO_GITPUSH', // todo: deprecate
|
||||
OCO_HOOK_AUTO_UNCOMMENT = 'OCO_HOOK_AUTO_UNCOMMENT'
|
||||
}
|
||||
|
||||
export enum CONFIG_MODES {
|
||||
@@ -132,9 +133,113 @@ export const MODEL_LIST = {
|
||||
'mistral-moderation-2411',
|
||||
'mistral-moderation-latest'
|
||||
],
|
||||
|
||||
deepseek: ['deepseek-chat', 'deepseek-reasoner'],
|
||||
|
||||
// AI/ML API available chat-completion models
|
||||
// https://api.aimlapi.com/v1/models
|
||||
aimlapi: [
|
||||
'openai/gpt-4o',
|
||||
'gpt-4o-2024-08-06',
|
||||
'gpt-4o-2024-05-13',
|
||||
'gpt-4o-mini',
|
||||
'gpt-4o-mini-2024-07-18',
|
||||
'chatgpt-4o-latest',
|
||||
'gpt-4-turbo',
|
||||
'gpt-4-turbo-2024-04-09',
|
||||
'gpt-4',
|
||||
'gpt-4-0125-preview',
|
||||
'gpt-4-1106-preview',
|
||||
'gpt-3.5-turbo',
|
||||
'gpt-3.5-turbo-0125',
|
||||
'gpt-3.5-turbo-1106',
|
||||
'o1-preview',
|
||||
'o1-preview-2024-09-12',
|
||||
'o1-mini',
|
||||
'o1-mini-2024-09-12',
|
||||
'o3-mini',
|
||||
'gpt-4o-audio-preview',
|
||||
'gpt-4o-mini-audio-preview',
|
||||
'gpt-4o-search-preview',
|
||||
'gpt-4o-mini-search-preview',
|
||||
'openai/gpt-4.1-2025-04-14',
|
||||
'openai/gpt-4.1-mini-2025-04-14',
|
||||
'openai/gpt-4.1-nano-2025-04-14',
|
||||
'openai/o4-mini-2025-04-16',
|
||||
'openai/o3-2025-04-16',
|
||||
'o1',
|
||||
'openai/o3-pro',
|
||||
'meta-llama/Llama-3.2-90B-Vision-Instruct-Turbo',
|
||||
'google/gemma-2-27b-it',
|
||||
'meta-llama/Llama-Vision-Free',
|
||||
'Qwen/Qwen2-72B-Instruct',
|
||||
'mistralai/Mixtral-8x7B-Instruct-v0.1',
|
||||
'nvidia/Llama-3.1-Nemotron-70B-Instruct-HF',
|
||||
'NousResearch/Nous-Hermes-2-Mixtral-8x7B-DPO',
|
||||
'meta-llama/Llama-3.3-70B-Instruct-Turbo',
|
||||
'meta-llama/Llama-3.2-3B-Instruct-Turbo',
|
||||
'meta-llama/Llama-3.2-11B-Vision-Instruct-Turbo',
|
||||
'meta-llama/Llama-Guard-3-11B-Vision-Turbo',
|
||||
'Qwen/Qwen2.5-7B-Instruct-Turbo',
|
||||
'Qwen/Qwen2.5-Coder-32B-Instruct',
|
||||
'meta-llama/Meta-Llama-3-8B-Instruct-Lite',
|
||||
'meta-llama/Llama-3-8b-chat-hf',
|
||||
'meta-llama/Llama-3-70b-chat-hf',
|
||||
'Qwen/Qwen2.5-72B-Instruct-Turbo',
|
||||
'Qwen/QwQ-32B',
|
||||
'meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo',
|
||||
'meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo',
|
||||
'meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo',
|
||||
'mistralai/Mistral-7B-Instruct-v0.2',
|
||||
'meta-llama/LlamaGuard-2-8b',
|
||||
'mistralai/Mistral-7B-Instruct-v0.1',
|
||||
'mistralai/Mistral-7B-Instruct-v0.3',
|
||||
'meta-llama/Meta-Llama-Guard-3-8B',
|
||||
'meta-llama/llama-4-scout',
|
||||
'meta-llama/llama-4-maverick',
|
||||
'Qwen/Qwen3-235B-A22B-fp8-tput',
|
||||
'claude-3-opus-20240229',
|
||||
'claude-3-haiku-20240307',
|
||||
'claude-3-5-sonnet-20240620',
|
||||
'claude-3-5-sonnet-20241022',
|
||||
'claude-3-5-haiku-20241022',
|
||||
'claude-3-7-sonnet-20250219',
|
||||
'claude-sonnet-4-20250514',
|
||||
'claude-opus-4-20250514',
|
||||
'google/gemini-2.0-flash-exp',
|
||||
'google/gemini-2.0-flash',
|
||||
'google/gemini-2.5-pro',
|
||||
'google/gemini-2.5-flash',
|
||||
'deepseek-chat',
|
||||
'deepseek-reasoner',
|
||||
'qwen-max',
|
||||
'qwen-plus',
|
||||
'qwen-turbo',
|
||||
'qwen-max-2025-01-25',
|
||||
'mistralai/mistral-tiny',
|
||||
'mistralai/mistral-nemo',
|
||||
'anthracite-org/magnum-v4-72b',
|
||||
'nvidia/llama-3.1-nemotron-70b-instruct',
|
||||
'cohere/command-r-plus',
|
||||
'mistralai/codestral-2501',
|
||||
'google/gemma-3-4b-it',
|
||||
'google/gemma-3-12b-it',
|
||||
'google/gemma-3-27b-it',
|
||||
'google/gemini-2.5-flash-lite-preview',
|
||||
'deepseek/deepseek-prover-v2',
|
||||
'google/gemma-3n-e4b-it',
|
||||
'cohere/command-a',
|
||||
'MiniMax-Text-01',
|
||||
'abab6.5s-chat',
|
||||
'minimax/m1',
|
||||
'bagoodex/bagoodex-search-v1',
|
||||
'moonshot/kimi-k2-preview',
|
||||
'perplexity/sonar',
|
||||
'perplexity/sonar-pro',
|
||||
'x-ai/grok-4-07-09',
|
||||
'x-ai/grok-3-beta',
|
||||
'x-ai/grok-3-mini-beta'
|
||||
],
|
||||
|
||||
// OpenRouter available models
|
||||
// input_modalities: 'text'
|
||||
// output_modalities: 'text'
|
||||
@@ -483,6 +588,8 @@ const getDefaultModel = (provider: string | undefined): string => {
|
||||
return MODEL_LIST.mistral[0];
|
||||
case 'deepseek':
|
||||
return MODEL_LIST.deepseek[0];
|
||||
case 'aimlapi':
|
||||
return MODEL_LIST.aimlapi[0];
|
||||
case 'openrouter':
|
||||
return MODEL_LIST.openrouter[0];
|
||||
default:
|
||||
@@ -675,9 +782,10 @@ export const configValidators = {
|
||||
'flowise',
|
||||
'groq',
|
||||
'deepseek',
|
||||
'aimlapi',
|
||||
'openrouter'
|
||||
].includes(value) || value.startsWith('ollama'),
|
||||
`${value} is not supported yet, use 'ollama', 'mlx', 'anthropic', 'azure', 'gemini', 'flowise', 'mistral', 'deepseek' or 'openai' (default)`
|
||||
`${value} is not supported yet, use 'ollama', 'mlx', 'anthropic', 'azure', 'gemini', 'flowise', 'mistral', 'deepseek', 'aimlapi' or 'openai' (default)`
|
||||
);
|
||||
|
||||
return value;
|
||||
@@ -711,6 +819,14 @@ export const configValidators = {
|
||||
'Must be true or false'
|
||||
);
|
||||
return value;
|
||||
},
|
||||
|
||||
[CONFIG_KEYS.OCO_HOOK_AUTO_UNCOMMENT](value: any) {
|
||||
validateConfig(
|
||||
CONFIG_KEYS.OCO_HOOK_AUTO_UNCOMMENT,
|
||||
typeof value === 'boolean',
|
||||
'Must be true or false'
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -726,6 +842,7 @@ export enum OCO_AI_PROVIDER_ENUM {
|
||||
MISTRAL = 'mistral',
|
||||
MLX = 'mlx',
|
||||
DEEPSEEK = 'deepseek',
|
||||
AIMLAPI = 'aimlapi',
|
||||
OPENROUTER = 'openrouter'
|
||||
}
|
||||
|
||||
@@ -747,6 +864,7 @@ export type ConfigType = {
|
||||
[CONFIG_KEYS.OCO_ONE_LINE_COMMIT]: boolean;
|
||||
[CONFIG_KEYS.OCO_OMIT_SCOPE]: boolean;
|
||||
[CONFIG_KEYS.OCO_TEST_MOCK_TYPE]: string;
|
||||
[CONFIG_KEYS.OCO_HOOK_AUTO_UNCOMMENT]: boolean;
|
||||
};
|
||||
|
||||
export const defaultConfigPath = pathJoin(homedir(), '.opencommit');
|
||||
@@ -794,7 +912,8 @@ export const DEFAULT_CONFIG = {
|
||||
OCO_TEST_MOCK_TYPE: 'commit-message',
|
||||
OCO_WHY: false,
|
||||
OCO_OMIT_SCOPE: false,
|
||||
OCO_GITPUSH: true // todo: deprecate
|
||||
OCO_GITPUSH: true, // todo: deprecate
|
||||
OCO_HOOK_AUTO_UNCOMMENT: false
|
||||
};
|
||||
|
||||
const initGlobalConfig = (configPath: string = defaultConfigPath) => {
|
||||
@@ -1046,6 +1165,11 @@ function getConfigKeyDetails(key) {
|
||||
description: 'Message template placeholder',
|
||||
values: ['String (must start with $)']
|
||||
};
|
||||
case CONFIG_KEYS.OCO_HOOK_AUTO_UNCOMMENT:
|
||||
return {
|
||||
description: 'Automatically uncomment the commit message in the hook',
|
||||
values: ['true', 'false']
|
||||
};
|
||||
default:
|
||||
return {
|
||||
description: 'String value',
|
||||
|
||||
@@ -56,10 +56,14 @@ export const prepareCommitMessageHook = async (
|
||||
|
||||
const fileContent = await fs.readFile(messageFilePath);
|
||||
|
||||
await fs.writeFile(
|
||||
messageFilePath,
|
||||
commitMessage + '\n' + fileContent.toString()
|
||||
);
|
||||
const messageWithComment = `# ${commitMessage}\n\n# ---------- [OpenCommit] ---------- #\n# Remove the # above to use this generated commit message.\n# To cancel the commit, just close this window without making any changes.\n\n${fileContent.toString()}`;
|
||||
const messageWithoutComment = `${commitMessage}\n\n${fileContent.toString()}`;
|
||||
|
||||
const message = config.OCO_HOOK_AUTO_UNCOMMENT
|
||||
? messageWithoutComment
|
||||
: messageWithComment;
|
||||
|
||||
await fs.writeFile(messageFilePath, message);
|
||||
} catch (error) {
|
||||
outro(`${chalk.red('✖')} ${error}`);
|
||||
process.exit(1);
|
||||
|
||||
47
src/engine/aimlapi.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import OpenAI from 'openai';
|
||||
import axios, { AxiosInstance } from 'axios';
|
||||
import { AiEngine, AiEngineConfig } from './Engine';
|
||||
|
||||
interface AimlApiConfig extends AiEngineConfig {}
|
||||
|
||||
export class AimlApiEngine implements AiEngine {
|
||||
client: AxiosInstance;
|
||||
|
||||
constructor(public config: AimlApiConfig) {
|
||||
this.client = axios.create({
|
||||
baseURL: config.baseURL || 'https://api.aimlapi.com/v1/chat/completions',
|
||||
headers: {
|
||||
Authorization: `Bearer ${config.apiKey}`,
|
||||
'HTTP-Referer': 'https://github.com/di-sukharev/opencommit',
|
||||
'X-Title': 'opencommit',
|
||||
'Content-Type': 'application/json',
|
||||
...config.customHeaders
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public generateCommitMessage = async (
|
||||
messages: Array<OpenAI.Chat.Completions.ChatCompletionMessageParam>
|
||||
): Promise<string | null> => {
|
||||
try {
|
||||
const response = await this.client.post('', {
|
||||
model: this.config.model,
|
||||
messages
|
||||
});
|
||||
|
||||
const message = response.data.choices?.[0]?.message;
|
||||
return message?.content ?? null;
|
||||
} catch (error) {
|
||||
const err = error as Error;
|
||||
if (
|
||||
axios.isAxiosError<{ error?: { message: string } }>(error) &&
|
||||
error.response?.status === 401
|
||||
) {
|
||||
const apiError = error.response.data.error;
|
||||
if (apiError) throw new Error(apiError.message);
|
||||
}
|
||||
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -53,7 +53,7 @@ export class AzureEngine implements AiEngine {
|
||||
if (message?.content === null) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
||||
let content = message?.content;
|
||||
return removeContentTags(content, 'think');
|
||||
} catch (error) {
|
||||
|
||||
@@ -7,4 +7,4 @@ export class GroqEngine extends OpenAiEngine {
|
||||
config.baseURL = 'https://api.groq.com/openai/v1';
|
||||
super(config);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,10 @@ export class MistralAiEngine implements AiEngine {
|
||||
if (!config.baseURL) {
|
||||
this.client = new Mistral({ apiKey: config.apiKey });
|
||||
} else {
|
||||
this.client = new Mistral({ apiKey: config.apiKey, serverURL: config.baseURL });
|
||||
this.client = new Mistral({
|
||||
apiKey: config.apiKey,
|
||||
serverURL: config.baseURL
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,13 +53,12 @@ export class MistralAiEngine implements AiEngine {
|
||||
|
||||
const completion = await this.client.chat.complete(params);
|
||||
|
||||
if (!completion.choices)
|
||||
throw Error('No completion choice available.')
|
||||
|
||||
if (!completion.choices) throw Error('No completion choice available.');
|
||||
|
||||
const message = completion.choices[0].message;
|
||||
|
||||
if (!message || !message.content)
|
||||
throw Error('No completion choice available.')
|
||||
throw Error('No completion choice available.');
|
||||
|
||||
let content = message.content as string;
|
||||
return removeContentTags(content, 'think');
|
||||
|
||||
@@ -6,42 +6,42 @@ import { AiEngine, AiEngineConfig } from './Engine';
|
||||
interface MLXConfig extends AiEngineConfig {}
|
||||
|
||||
export class MLXEngine implements AiEngine {
|
||||
config: MLXConfig;
|
||||
client: AxiosInstance;
|
||||
config: MLXConfig;
|
||||
client: AxiosInstance;
|
||||
|
||||
constructor(config) {
|
||||
this.config = config;
|
||||
this.client = axios.create({
|
||||
url: config.baseURL
|
||||
? `${config.baseURL}/${config.apiKey}`
|
||||
: 'http://localhost:8080/v1/chat/completions',
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
});
|
||||
constructor(config) {
|
||||
this.config = config;
|
||||
this.client = axios.create({
|
||||
url: config.baseURL
|
||||
? `${config.baseURL}/${config.apiKey}`
|
||||
: 'http://localhost:8080/v1/chat/completions',
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
});
|
||||
}
|
||||
|
||||
async generateCommitMessage(
|
||||
messages: Array<OpenAI.Chat.Completions.ChatCompletionMessageParam>
|
||||
): Promise<string | undefined> {
|
||||
const params = {
|
||||
messages,
|
||||
temperature: 0,
|
||||
top_p: 0.1,
|
||||
repetition_penalty: 1.5,
|
||||
stream: false
|
||||
};
|
||||
try {
|
||||
const response = await this.client.post(
|
||||
this.client.getUri(this.config),
|
||||
params
|
||||
);
|
||||
|
||||
const choices = response.data.choices;
|
||||
const message = choices[0].message;
|
||||
let content = message?.content;
|
||||
return removeContentTags(content, 'think');
|
||||
} catch (err: any) {
|
||||
const message = err.response?.data?.error ?? err.message;
|
||||
throw new Error(`MLX provider error: ${message}`);
|
||||
}
|
||||
|
||||
async generateCommitMessage(
|
||||
messages: Array<OpenAI.Chat.Completions.ChatCompletionMessageParam>):
|
||||
Promise<string | undefined> {
|
||||
const params = {
|
||||
messages,
|
||||
temperature: 0,
|
||||
top_p: 0.1,
|
||||
repetition_penalty: 1.5,
|
||||
stream: false
|
||||
};
|
||||
try {
|
||||
const response = await this.client.post(
|
||||
this.client.getUri(this.config),
|
||||
params
|
||||
);
|
||||
|
||||
const choices = response.data.choices;
|
||||
const message = choices[0].message;
|
||||
let content = message?.content;
|
||||
return removeContentTags(content, 'think');
|
||||
} catch (err: any) {
|
||||
const message = err.response?.data?.error ?? err.message;
|
||||
throw new Error(`MLX provider error: ${message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,13 +11,13 @@ export class OllamaEngine implements AiEngine {
|
||||
|
||||
constructor(config) {
|
||||
this.config = config;
|
||||
|
||||
|
||||
// Combine base headers with custom headers
|
||||
const headers = {
|
||||
const headers = {
|
||||
'Content-Type': 'application/json',
|
||||
...config.customHeaders
|
||||
...config.customHeaders
|
||||
};
|
||||
|
||||
|
||||
this.client = axios.create({
|
||||
url: config.baseURL
|
||||
? `${config.baseURL}/${config.apiKey}`
|
||||
|
||||
@@ -18,18 +18,18 @@ export class OpenAiEngine implements AiEngine {
|
||||
const clientOptions: OpenAI.ClientOptions = {
|
||||
apiKey: config.apiKey
|
||||
};
|
||||
|
||||
|
||||
if (config.baseURL) {
|
||||
clientOptions.baseURL = config.baseURL;
|
||||
}
|
||||
|
||||
|
||||
if (config.customHeaders) {
|
||||
const headers = parseCustomHeaders(config.customHeaders);
|
||||
if (Object.keys(headers).length > 0) {
|
||||
clientOptions.defaultHeaders = headers;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
this.client = new OpenAI(clientOptions);
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ export class OpenAiEngine implements AiEngine {
|
||||
this.config.maxTokensInput - this.config.maxTokensOutput
|
||||
)
|
||||
throw new Error(GenerateCommitMessageErrorEnum.tooMuchTokens);
|
||||
|
||||
|
||||
const completion = await this.client.chat.completions.create(params);
|
||||
|
||||
const message = completion.choices[0].message;
|
||||
|
||||
@@ -14,7 +14,10 @@ const generateCommitMessageChatCompletionPrompt = async (
|
||||
fullGitMojiSpec: boolean,
|
||||
context: string
|
||||
): Promise<Array<OpenAI.Chat.Completions.ChatCompletionMessageParam>> => {
|
||||
const INIT_MESSAGES_PROMPT = await getMainCommitPrompt(fullGitMojiSpec, context);
|
||||
const INIT_MESSAGES_PROMPT = await getMainCommitPrompt(
|
||||
fullGitMojiSpec,
|
||||
context
|
||||
);
|
||||
|
||||
const chatContextAsCompletionRequest = [...INIT_MESSAGES_PROMPT];
|
||||
|
||||
@@ -38,7 +41,7 @@ const ADJUSTMENT_FACTOR = 20;
|
||||
export const generateCommitMessageByDiff = async (
|
||||
diff: string,
|
||||
fullGitMojiSpec: boolean = false,
|
||||
context: string = ""
|
||||
context: string = ''
|
||||
): Promise<string> => {
|
||||
try {
|
||||
const INIT_MESSAGES_PROMPT = await getMainCommitPrompt(
|
||||
@@ -75,7 +78,7 @@ export const generateCommitMessageByDiff = async (
|
||||
const messages = await generateCommitMessageChatCompletionPrompt(
|
||||
diff,
|
||||
fullGitMojiSpec,
|
||||
context,
|
||||
context
|
||||
);
|
||||
|
||||
const engine = getEngine();
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"localLanguage": "한국어",
|
||||
"commitFix": "fix(server.ts): 포트 변수를 소문자 port에서 대문자 PORT로 변경",
|
||||
"commitFeat": "피트(server.ts): process.env.PORT 환경 변수 지원 추가",
|
||||
"commitFeat": "feat(server.ts): process.env.PORT 환경 변수 지원 추가",
|
||||
"commitDescription": "포트 변수는 이제 PORT로 이름이 지정되어 상수인 PORT와 일관성 있는 이름 규칙을 따릅니다. 환경 변수 지원을 통해 애플리케이션은 이제 process.env.PORT 환경 변수로 지정된 사용 가능한 모든 포트에서 실행할 수 있으므로 더 유연해졌습니다.",
|
||||
"commitFixOmitScope": "fix: 포트 변수를 소문자 port에서 대문자 PORT로 변경",
|
||||
"commitFeatOmitScope": "피트: process.env.PORT 환경 변수 지원 추가"
|
||||
"commitFeatOmitScope": "feat: process.env.PORT 환경 변수 지원 추가"
|
||||
}
|
||||
|
||||
@@ -36,6 +36,19 @@ export const runMigrations = async () => {
|
||||
const config = getConfig();
|
||||
if (config.OCO_AI_PROVIDER === OCO_AI_PROVIDER_ENUM.TEST) return;
|
||||
|
||||
// skip unhandled providers in migration00
|
||||
if (
|
||||
[
|
||||
OCO_AI_PROVIDER_ENUM.DEEPSEEK,
|
||||
OCO_AI_PROVIDER_ENUM.GROQ,
|
||||
OCO_AI_PROVIDER_ENUM.MISTRAL,
|
||||
OCO_AI_PROVIDER_ENUM.MLX,
|
||||
OCO_AI_PROVIDER_ENUM.OPENROUTER
|
||||
].includes(config.OCO_AI_PROVIDER)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const completedMigrations = getCompletedMigrations();
|
||||
|
||||
let isMigrated = false;
|
||||
|
||||
@@ -56,10 +56,11 @@ const llmReadableRules: {
|
||||
blankline: (key, applicable) =>
|
||||
`There should ${applicable} be a blank line at the beginning of the ${key}.`,
|
||||
caseRule: (key, applicable, value: string | Array<string>) =>
|
||||
`The ${key} should ${applicable} be in ${Array.isArray(value)
|
||||
? `one of the following case:
|
||||
`The ${key} should ${applicable} be in ${
|
||||
Array.isArray(value)
|
||||
? `one of the following case:
|
||||
- ${value.join('\n - ')}.`
|
||||
: `${value} case.`
|
||||
: `${value} case.`
|
||||
}`,
|
||||
emptyRule: (key, applicable) => `The ${key} should ${applicable} be empty.`,
|
||||
enumRule: (key, applicable, value: string | Array<string>) =>
|
||||
@@ -67,17 +68,18 @@ const llmReadableRules: {
|
||||
- ${Array.isArray(value) ? value.join('\n - ') : value}.`,
|
||||
enumTypeRule: (key, applicable, value: string | Array<string>, prompt) =>
|
||||
`The ${key} should ${applicable} be one of the following values:
|
||||
- ${Array.isArray(value)
|
||||
- ${
|
||||
Array.isArray(value)
|
||||
? value
|
||||
.map((v) => {
|
||||
const description = getTypeRuleExtraDescription(v, prompt);
|
||||
if (description) {
|
||||
return `${v} (${description})`;
|
||||
} else return v;
|
||||
})
|
||||
.join('\n - ')
|
||||
.map((v) => {
|
||||
const description = getTypeRuleExtraDescription(v, prompt);
|
||||
if (description) {
|
||||
return `${v} (${description})`;
|
||||
} else return v;
|
||||
})
|
||||
.join('\n - ')
|
||||
: value
|
||||
}.`,
|
||||
}.`,
|
||||
fullStopRule: (key, applicable, value: string) =>
|
||||
`The ${key} should ${applicable} end with '${value}'.`,
|
||||
maxLengthRule: (key, applicable, value: string) =>
|
||||
@@ -214,16 +216,20 @@ const STRUCTURE_OF_COMMIT = config.OCO_OMIT_SCOPE
|
||||
const GEN_COMMITLINT_CONSISTENCY_PROMPT = (
|
||||
prompts: string[]
|
||||
): OpenAI.Chat.Completions.ChatCompletionMessageParam[] => [
|
||||
{
|
||||
role: 'system',
|
||||
content: `${IDENTITY} Your mission is to create clean and comprehensive commit messages for two different changes in a single codebase and output them in the provided JSON format: one for a bug fix and another for a new feature.
|
||||
{
|
||||
role: 'system',
|
||||
content: `${IDENTITY} Your mission is to create clean and comprehensive commit messages for two different changes in a single codebase and output them in the provided JSON format: one for a bug fix and another for a new feature.
|
||||
|
||||
Here are the specific requirements and conventions that should be strictly followed:
|
||||
|
||||
Commit Message Conventions:
|
||||
- The commit message consists of three parts: Header, Body, and Footer.
|
||||
- Header:
|
||||
- Format: ${config.OCO_OMIT_SCOPE ? '`<type>: <subject>`' : '`<type>(<scope>): <subject>`'}
|
||||
- Format: ${
|
||||
config.OCO_OMIT_SCOPE
|
||||
? '`<type>: <subject>`'
|
||||
: '`<type>(<scope>): <subject>`'
|
||||
}
|
||||
- ${prompts.join('\n- ')}
|
||||
|
||||
JSON Output Format:
|
||||
@@ -246,9 +252,9 @@ Additional Details:
|
||||
- Allowing the server to listen on a port specified through the environment variable is considered a new feature.
|
||||
|
||||
Example Git Diff is to follow:`
|
||||
},
|
||||
INIT_DIFF_PROMPT
|
||||
];
|
||||
},
|
||||
INIT_DIFF_PROMPT
|
||||
];
|
||||
|
||||
/**
|
||||
* Prompt to have LLM generate a message using @commitlint rules.
|
||||
@@ -262,25 +268,30 @@ const INIT_MAIN_PROMPT = (
|
||||
prompts: string[]
|
||||
): OpenAI.Chat.Completions.ChatCompletionMessageParam => ({
|
||||
role: 'system',
|
||||
content: `${IDENTITY} Your mission is to create clean and comprehensive commit messages in the given @commitlint convention and explain WHAT were the changes ${config.OCO_WHY ? '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."
|
||||
}
|
||||
content: `${IDENTITY} Your mission is to create clean and comprehensive commit messages in the given @commitlint convention and explain WHAT were the changes ${
|
||||
config.OCO_WHY ? '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. Use ${language} to answer.
|
||||
${config.OCO_ONE_LINE_COMMIT
|
||||
? 'Craft a concise commit message that encapsulates all changes made, with an emphasis on the primary updates. If the modifications share a common theme or scope, mention it succinctly; otherwise, leave the scope out to maintain focus. The goal is to provide a clear and unified overview of the changes in a one single message, without diverging into a list of commit per file change.'
|
||||
: ''
|
||||
}
|
||||
${config.OCO_OMIT_SCOPE
|
||||
? 'Do not include a scope in the commit message format. Use the format: <type>: <subject>'
|
||||
: ''
|
||||
}
|
||||
${
|
||||
config.OCO_ONE_LINE_COMMIT
|
||||
? 'Craft a concise commit message that encapsulates all changes made, with an emphasis on the primary updates. If the modifications share a common theme or scope, mention it succinctly; otherwise, leave the scope out to maintain focus. The goal is to provide a clear and unified overview of the changes in a one single message, without diverging into a list of commit per file change.'
|
||||
: ''
|
||||
}
|
||||
${
|
||||
config.OCO_OMIT_SCOPE
|
||||
? 'Do not include a scope in the commit message format. Use the format: <type>: <subject>'
|
||||
: ''
|
||||
}
|
||||
You will strictly follow the following conventions to generate the content of the commit message:
|
||||
- ${prompts.join('\n- ')}
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ export const getJSONBlock = (input: string): string => {
|
||||
if (jsonIndex > -1) {
|
||||
input = input.slice(jsonIndex + 8);
|
||||
const endJsonIndex = input.search('```');
|
||||
input = input.slice(0, endJsonIndex);
|
||||
input = input.slice(0, endJsonIndex);
|
||||
}
|
||||
return input;
|
||||
};
|
||||
|
||||
@@ -155,9 +155,9 @@ const INIT_MAIN_PROMPT = (
|
||||
});
|
||||
|
||||
export const INIT_DIFF_PROMPT: OpenAI.Chat.Completions.ChatCompletionMessageParam =
|
||||
{
|
||||
role: 'user',
|
||||
content: `diff --git a/src/server.ts b/src/server.ts
|
||||
{
|
||||
role: 'user',
|
||||
content: `diff --git a/src/server.ts b/src/server.ts
|
||||
index ad4db42..f3b18a9 100644
|
||||
--- a/src/server.ts
|
||||
+++ b/src/server.ts
|
||||
@@ -181,7 +181,7 @@ export const INIT_DIFF_PROMPT: OpenAI.Chat.Completions.ChatCompletionMessagePara
|
||||
+app.listen(process.env.PORT || PORT, () => {
|
||||
+ console.log(\`Server listening on port \${PORT}\`);
|
||||
});`
|
||||
};
|
||||
};
|
||||
|
||||
const COMMIT_TYPES = {
|
||||
fix: '🐛',
|
||||
@@ -193,19 +193,19 @@ const generateCommitString = (
|
||||
message: string
|
||||
): string => {
|
||||
const cleanMessage = removeConventionalCommitWord(message);
|
||||
return config.OCO_EMOJI
|
||||
? `${COMMIT_TYPES[type]} ${cleanMessage}`
|
||||
: message;
|
||||
return config.OCO_EMOJI ? `${COMMIT_TYPES[type]} ${cleanMessage}` : message;
|
||||
};
|
||||
|
||||
const getConsistencyContent = (translation: ConsistencyPrompt) => {
|
||||
const fixMessage = config.OCO_OMIT_SCOPE && translation.commitFixOmitScope
|
||||
? translation.commitFixOmitScope
|
||||
: translation.commitFix;
|
||||
const fixMessage =
|
||||
config.OCO_OMIT_SCOPE && translation.commitFixOmitScope
|
||||
? translation.commitFixOmitScope
|
||||
: translation.commitFix;
|
||||
|
||||
const featMessage = config.OCO_OMIT_SCOPE && translation.commitFeatOmitScope
|
||||
? translation.commitFeatOmitScope
|
||||
: translation.commitFeat;
|
||||
const featMessage =
|
||||
config.OCO_OMIT_SCOPE && translation.commitFeatOmitScope
|
||||
? translation.commitFeatOmitScope
|
||||
: translation.commitFeat;
|
||||
|
||||
const fix = generateCommitString('fix', fixMessage);
|
||||
const feat = config.OCO_ONE_LINE_COMMIT
|
||||
@@ -250,7 +250,7 @@ export const getMainCommitPrompt = async (
|
||||
INIT_DIFF_PROMPT,
|
||||
INIT_CONSISTENCY_PROMPT(
|
||||
commitLintConfig.consistency[
|
||||
translation.localLanguage
|
||||
translation.localLanguage
|
||||
] as ConsistencyPrompt
|
||||
)
|
||||
];
|
||||
|
||||
@@ -11,6 +11,7 @@ import { TestAi, TestMockType } from '../engine/testAi';
|
||||
import { GroqEngine } from '../engine/groq';
|
||||
import { MLXEngine } from '../engine/mlx';
|
||||
import { DeepseekEngine } from '../engine/deepseek';
|
||||
import { AimlApiEngine } from '../engine/aimlapi';
|
||||
import { OpenRouterEngine } from '../engine/openrouter';
|
||||
|
||||
export function parseCustomHeaders(headers: any): Record<string, string> {
|
||||
@@ -81,6 +82,9 @@ export function getEngine(): AiEngine {
|
||||
case OCO_AI_PROVIDER_ENUM.DEEPSEEK:
|
||||
return new DeepseekEngine(DEFAULT_CONFIG);
|
||||
|
||||
case OCO_AI_PROVIDER_ENUM.AIMLAPI:
|
||||
return new AimlApiEngine(DEFAULT_CONFIG);
|
||||
|
||||
case OCO_AI_PROVIDER_ENUM.OPENROUTER:
|
||||
return new OpenRouterEngine(DEFAULT_CONFIG);
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { execa } from 'execa';
|
||||
import { readFileSync } from 'fs';
|
||||
import ignore, { Ignore } from 'ignore';
|
||||
|
||||
import { join } from 'path';
|
||||
import { outro, spinner } from '@clack/prompts';
|
||||
|
||||
export const assertGitRepo = async () => {
|
||||
@@ -16,41 +16,44 @@ export const assertGitRepo = async () => {
|
||||
// (file) => `:(exclude)${file}`
|
||||
// );
|
||||
|
||||
export const getOpenCommitIgnore = (): Ignore => {
|
||||
export const getOpenCommitIgnore = async (): Promise<Ignore> => {
|
||||
const gitDir = await getGitDir();
|
||||
|
||||
const ig = ignore();
|
||||
|
||||
try {
|
||||
ig.add(readFileSync('.opencommitignore').toString().split('\n'));
|
||||
ig.add(
|
||||
readFileSync(join(gitDir, '.opencommitignore')).toString().split('\n')
|
||||
);
|
||||
} catch (e) {}
|
||||
|
||||
return ig;
|
||||
};
|
||||
|
||||
export const getCoreHooksPath = async (): Promise<string> => {
|
||||
const { stdout } = await execa('git', ['config', 'core.hooksPath']);
|
||||
const gitDir = await getGitDir();
|
||||
|
||||
const { stdout } = await execa('git', ['config', 'core.hooksPath'], {
|
||||
cwd: gitDir
|
||||
});
|
||||
|
||||
return stdout;
|
||||
};
|
||||
|
||||
export const getStagedFiles = async (): Promise<string[]> => {
|
||||
const { stdout: gitDir } = await execa('git', [
|
||||
'rev-parse',
|
||||
'--show-toplevel'
|
||||
]);
|
||||
const gitDir = await getGitDir();
|
||||
|
||||
const { stdout: files } = await execa('git', [
|
||||
'diff',
|
||||
'--name-only',
|
||||
'--cached',
|
||||
'--relative',
|
||||
gitDir
|
||||
]);
|
||||
const { stdout: files } = await execa(
|
||||
'git',
|
||||
['diff', '--name-only', '--cached', '--relative'],
|
||||
{ cwd: gitDir }
|
||||
);
|
||||
|
||||
if (!files) return [];
|
||||
|
||||
const filesList = files.split('\n');
|
||||
|
||||
const ig = getOpenCommitIgnore();
|
||||
const ig = await getOpenCommitIgnore();
|
||||
const allowedFiles = filesList.filter((file) => !ig.ignores(file));
|
||||
|
||||
if (!allowedFiles) return [];
|
||||
@@ -59,12 +62,17 @@ export const getStagedFiles = async (): Promise<string[]> => {
|
||||
};
|
||||
|
||||
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 gitDir = await getGitDir();
|
||||
|
||||
const { stdout: modified } = await execa('git', ['ls-files', '--modified'], {
|
||||
cwd: gitDir
|
||||
});
|
||||
|
||||
const { stdout: others } = await execa(
|
||||
'git',
|
||||
['ls-files', '--others', '--exclude-standard'],
|
||||
{ cwd: gitDir }
|
||||
);
|
||||
|
||||
const files = [...modified.split('\n'), ...others.split('\n')].filter(
|
||||
(file) => !!file
|
||||
@@ -74,16 +82,20 @@ export const getChangedFiles = async (): Promise<string[]> => {
|
||||
};
|
||||
|
||||
export const gitAdd = async ({ files }: { files: string[] }) => {
|
||||
const gitDir = await getGitDir();
|
||||
|
||||
const gitAddSpinner = spinner();
|
||||
|
||||
gitAddSpinner.start('Adding files to commit');
|
||||
|
||||
await execa('git', ['add', ...files]);
|
||||
await execa('git', ['add', ...files], { cwd: gitDir });
|
||||
|
||||
gitAddSpinner.stop(`Staged ${files.length} files`);
|
||||
};
|
||||
|
||||
export const getDiff = async ({ files }: { files: string[] }) => {
|
||||
const gitDir = await getGitDir();
|
||||
|
||||
const lockFiles = files.filter(
|
||||
(file) =>
|
||||
file.includes('.lock') ||
|
||||
@@ -108,12 +120,20 @@ export const getDiff = async ({ files }: { files: string[] }) => {
|
||||
(file) => !file.includes('.lock') && !file.includes('-lock.')
|
||||
);
|
||||
|
||||
const { stdout: diff } = await execa('git', [
|
||||
'diff',
|
||||
'--staged',
|
||||
'--',
|
||||
...filesWithoutLocks
|
||||
]);
|
||||
const { stdout: diff } = await execa(
|
||||
'git',
|
||||
['diff', '--staged', '--', ...filesWithoutLocks],
|
||||
{ cwd: gitDir }
|
||||
);
|
||||
|
||||
return diff;
|
||||
};
|
||||
|
||||
export const getGitDir = async (): Promise<string> => {
|
||||
const { stdout: gitDir } = await execa('git', [
|
||||
'rev-parse',
|
||||
'--show-toplevel'
|
||||
]);
|
||||
|
||||
return gitDir;
|
||||
};
|
||||
|
||||
@@ -4,20 +4,23 @@
|
||||
* @param tag The tag name without angle brackets (e.g., 'think' for '<think></think>')
|
||||
* @returns The content with the specified tags and their contents removed, and trimmed
|
||||
*/
|
||||
export function removeContentTags<T extends string | null | undefined>(content: T, tag: string): T {
|
||||
export function removeContentTags<T extends string | null | undefined>(
|
||||
content: T,
|
||||
tag: string
|
||||
): T {
|
||||
if (!content || typeof content !== 'string') {
|
||||
return content;
|
||||
}
|
||||
|
||||
|
||||
// Dynamic implementation for other cases
|
||||
const openTag = `<${tag}>`;
|
||||
const closeTag = `</${tag}>`;
|
||||
|
||||
|
||||
// Parse the content and remove tags
|
||||
let result = '';
|
||||
let skipUntil: number | null = null;
|
||||
let depth = 0;
|
||||
|
||||
|
||||
for (let i = 0; i < content.length; i++) {
|
||||
// Check for opening tag
|
||||
if (content.substring(i, i + openTag.length) === openTag) {
|
||||
@@ -29,7 +32,10 @@ export function removeContentTags<T extends string | null | undefined>(content:
|
||||
}
|
||||
}
|
||||
// Check for closing tag
|
||||
else if (content.substring(i, i + closeTag.length) === closeTag && depth > 0) {
|
||||
else if (
|
||||
content.substring(i, i + closeTag.length) === closeTag &&
|
||||
depth > 0
|
||||
) {
|
||||
depth--;
|
||||
if (depth === 0) {
|
||||
i = i + closeTag.length - 1; // Skip the closing tag
|
||||
@@ -37,7 +43,7 @@ export function removeContentTags<T extends string | null | undefined>(content:
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Only add character if not inside a tag
|
||||
if (skipUntil === null) {
|
||||
result += content[i];
|
||||
|
||||