sncommit 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/index.js +93 -99
- package/package.json +5 -2
- package/bun.lock +0 -705
- package/eslint.config.mjs +0 -34
- package/src/components/App.tsx +0 -357
- package/src/components/CommitSuggestions.tsx +0 -157
- package/src/components/ConfigApp.tsx +0 -258
- package/src/components/CustomInputPrompt.tsx +0 -82
- package/src/components/StagedFiles.tsx +0 -52
- package/src/components/TuiDialog.tsx +0 -177
- package/src/index.tsx +0 -150
- package/src/services/git.ts +0 -128
- package/src/services/groq.ts +0 -360
- package/src/suppress-warnings.ts +0 -18
- package/src/types/index.ts +0 -36
- package/src/utils/config.ts +0 -66
- package/tsconfig.json +0 -19
package/README.md
CHANGED
package/dist/index.js
CHANGED
|
@@ -58323,7 +58323,15 @@ class GroqService {
|
|
|
58323
58323
|
}
|
|
58324
58324
|
return this.parseSuggestions(content);
|
|
58325
58325
|
} catch (e2) {
|
|
58326
|
-
|
|
58326
|
+
const error = e2;
|
|
58327
|
+
if (error?.status === 401) {
|
|
58328
|
+
console.warn(`
|
|
58329
|
+
\x1B[33m⚠️ Groq API Key is invalid.\x1B[0m`);
|
|
58330
|
+
console.warn(` Using static backup suggestions. Run \x1B[36mbetter-commit config\x1B[0m to set your key.
|
|
58331
|
+
`);
|
|
58332
|
+
} else {
|
|
58333
|
+
console.error("Error generation suggestions:", error?.message || String(e2));
|
|
58334
|
+
}
|
|
58327
58335
|
const fallbackSuggestions = this.getFallbackSuggestions(stagedFiles);
|
|
58328
58336
|
return fallbackSuggestions.map((s2) => ({ ...s2, isFallback: true }));
|
|
58329
58337
|
}
|
|
@@ -58363,7 +58371,15 @@ class GroqService {
|
|
|
58363
58371
|
}
|
|
58364
58372
|
return this.parseSuggestions(content);
|
|
58365
58373
|
} catch (e2) {
|
|
58366
|
-
|
|
58374
|
+
const error = e2;
|
|
58375
|
+
if (error?.status === 401) {
|
|
58376
|
+
console.warn(`
|
|
58377
|
+
\x1B[33m⚠️ Groq API Key is invalid.\x1B[0m`);
|
|
58378
|
+
console.warn(` Using static backup suggestions. Run \x1B[36mbetter-commit config\x1B[0m to set your key.
|
|
58379
|
+
`);
|
|
58380
|
+
} else {
|
|
58381
|
+
console.error("Error generation suggestions:", error?.message || String(e2));
|
|
58382
|
+
}
|
|
58367
58383
|
const fallbackSuggestions = this.getFallbackSuggestions(stagedFiles);
|
|
58368
58384
|
return fallbackSuggestions.map((s2) => ({ ...s2, isFallback: true }));
|
|
58369
58385
|
}
|
|
@@ -58510,11 +58526,13 @@ Output ONLY valid JSON.`;
|
|
|
58510
58526
|
if (jsonMatch) {
|
|
58511
58527
|
const parsed = JSON.parse(jsonMatch[0]);
|
|
58512
58528
|
if (parsed.suggestions && Array.isArray(parsed.suggestions)) {
|
|
58513
|
-
|
|
58514
|
-
|
|
58515
|
-
|
|
58516
|
-
|
|
58517
|
-
|
|
58529
|
+
if (parsed.suggestions && Array.isArray(parsed.suggestions)) {
|
|
58530
|
+
return parsed.suggestions.map((s2) => ({
|
|
58531
|
+
message: s2.message || "",
|
|
58532
|
+
type: s2.type || this.extractType(s2.message || ""),
|
|
58533
|
+
description: s2.description || s2.message || ""
|
|
58534
|
+
})).slice(0, 4);
|
|
58535
|
+
}
|
|
58518
58536
|
}
|
|
58519
58537
|
}
|
|
58520
58538
|
throw new Error("Invalid JSON structure");
|
|
@@ -58689,7 +58707,7 @@ var CommitSuggestions = ({
|
|
|
58689
58707
|
}, 80);
|
|
58690
58708
|
return () => clearInterval(timer);
|
|
58691
58709
|
}
|
|
58692
|
-
}, [isLoading]);
|
|
58710
|
+
}, [isLoading, spinnerFrames.length]);
|
|
58693
58711
|
use_input_default((input, key) => {
|
|
58694
58712
|
if (key.upArrow) {
|
|
58695
58713
|
const newIndex = selectedIndex > 0 ? selectedIndex - 1 : totalOptions - 1;
|
|
@@ -58933,12 +58951,16 @@ var BetterCommitApp = ({
|
|
|
58933
58951
|
if (successMessage) {
|
|
58934
58952
|
write("\x1B[2J\x1B[0f");
|
|
58935
58953
|
const timer = setTimeout(() => {
|
|
58936
|
-
onExit(successMessage);
|
|
58937
58954
|
exit();
|
|
58938
58955
|
}, 1500);
|
|
58939
58956
|
return () => clearTimeout(timer);
|
|
58940
58957
|
}
|
|
58941
|
-
}, [successMessage,
|
|
58958
|
+
}, [successMessage, exit, write]);
|
|
58959
|
+
import_react24.useEffect(() => {
|
|
58960
|
+
if (state.error) {
|
|
58961
|
+
exit();
|
|
58962
|
+
}
|
|
58963
|
+
}, [state.error, exit]);
|
|
58942
58964
|
use_input_default((input, key) => {
|
|
58943
58965
|
if (key.escape || key.ctrl && input === "c") {
|
|
58944
58966
|
onExit("Operation cancelled");
|
|
@@ -58952,7 +58974,7 @@ var BetterCommitApp = ({
|
|
|
58952
58974
|
try {
|
|
58953
58975
|
const stagedFiles = await gitService.getStagedFiles();
|
|
58954
58976
|
await gitService.getDiff();
|
|
58955
|
-
await
|
|
58977
|
+
await fetchSuggestions(stagedFiles);
|
|
58956
58978
|
setState((prev) => ({
|
|
58957
58979
|
...prev,
|
|
58958
58980
|
stagedFiles
|
|
@@ -58966,11 +58988,11 @@ var BetterCommitApp = ({
|
|
|
58966
58988
|
};
|
|
58967
58989
|
initializeApp();
|
|
58968
58990
|
}, []);
|
|
58969
|
-
const
|
|
58991
|
+
const fetchSuggestions = async (stagedFiles, customPrompt) => {
|
|
58970
58992
|
if (!config.groqApiKey || config.groqApiKey.trim() === "") {
|
|
58971
58993
|
setState((prev) => ({
|
|
58972
58994
|
...prev,
|
|
58973
|
-
error: 'Groq API key not configured. Run "better-commit
|
|
58995
|
+
error: 'Groq API key not configured. Run "better-commit config" to set it up.',
|
|
58974
58996
|
isLoading: false
|
|
58975
58997
|
}));
|
|
58976
58998
|
return;
|
|
@@ -58980,7 +59002,12 @@ var BetterCommitApp = ({
|
|
|
58980
59002
|
const diff2 = await gitService.getDiff();
|
|
58981
59003
|
const diffStats = await gitService.getDiffStats();
|
|
58982
59004
|
const recentCommits = await gitService.getRecentCommits(config.maxHistoryCommits || 50);
|
|
58983
|
-
|
|
59005
|
+
let suggestions;
|
|
59006
|
+
if (customPrompt) {
|
|
59007
|
+
suggestions = await groqService.generateCommitSuggestionsFromCustomInput(stagedFiles, diff2, customPrompt, recentCommits, diffStats);
|
|
59008
|
+
} else {
|
|
59009
|
+
suggestions = await groqService.generateCommitSuggestions(stagedFiles, diff2, recentCommits, diffStats);
|
|
59010
|
+
}
|
|
58984
59011
|
const hasFallback = suggestions.some((s2) => s2.isFallback);
|
|
58985
59012
|
setState((prev) => ({
|
|
58986
59013
|
...prev,
|
|
@@ -59016,7 +59043,7 @@ var BetterCommitApp = ({
|
|
|
59016
59043
|
};
|
|
59017
59044
|
const handleTryAgain = () => {
|
|
59018
59045
|
setState((prev) => ({ ...prev, isLoading: true, suggestions: [] }));
|
|
59019
|
-
|
|
59046
|
+
fetchSuggestions(state.stagedFiles);
|
|
59020
59047
|
};
|
|
59021
59048
|
const handleCustomInput = () => {
|
|
59022
59049
|
setIsCustomInputMode(true);
|
|
@@ -59034,82 +59061,26 @@ var BetterCommitApp = ({
|
|
|
59034
59061
|
}
|
|
59035
59062
|
setIsCustomInputMode(false);
|
|
59036
59063
|
setState((prev) => ({ ...prev, isLoading: true, suggestions: [] }));
|
|
59037
|
-
await
|
|
59038
|
-
};
|
|
59039
|
-
const generateSuggestionsFromCustomInput = async (userInput) => {
|
|
59040
|
-
if (!config.groqApiKey || config.groqApiKey.trim() === "") {
|
|
59041
|
-
setState((prev) => ({
|
|
59042
|
-
...prev,
|
|
59043
|
-
error: 'Groq API key not configured. Run "better-commit --config" to set it up.',
|
|
59044
|
-
isLoading: false
|
|
59045
|
-
}));
|
|
59046
|
-
return;
|
|
59047
|
-
}
|
|
59048
|
-
try {
|
|
59049
|
-
const groqService = new GroqService(config.groqApiKey, config);
|
|
59050
|
-
const diff2 = await gitService.getDiff();
|
|
59051
|
-
const diffStats = await gitService.getDiffStats();
|
|
59052
|
-
const recentCommits = await gitService.getRecentCommits(config.maxHistoryCommits || 50);
|
|
59053
|
-
const suggestions = await groqService.generateCommitSuggestionsFromCustomInput(state.stagedFiles, diff2, userInput, recentCommits, diffStats);
|
|
59054
|
-
const hasFallback = suggestions.some((s2) => s2.isFallback);
|
|
59055
|
-
setState((prev) => ({
|
|
59056
|
-
...prev,
|
|
59057
|
-
suggestions,
|
|
59058
|
-
isLoading: false,
|
|
59059
|
-
error: undefined
|
|
59060
|
-
}));
|
|
59061
|
-
setIsUsingFallback(hasFallback);
|
|
59062
|
-
} catch (error) {
|
|
59063
|
-
setState((prev) => ({
|
|
59064
|
-
...prev,
|
|
59065
|
-
error: `Failed to generate suggestions: ${error instanceof Error ? error.message : String(error)}`,
|
|
59066
|
-
isLoading: false
|
|
59067
|
-
}));
|
|
59068
|
-
}
|
|
59064
|
+
await fetchSuggestions(state.stagedFiles, customInput.trim());
|
|
59069
59065
|
};
|
|
59070
59066
|
if (state.error) {
|
|
59071
59067
|
return /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Box_default, {
|
|
59072
|
-
|
|
59068
|
+
borderStyle: "single",
|
|
59069
|
+
borderColor: "#ef4444",
|
|
59073
59070
|
padding: 2,
|
|
59071
|
+
marginBottom: 1,
|
|
59074
59072
|
children: [
|
|
59073
|
+
/* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Text, {
|
|
59074
|
+
color: "#ef4444",
|
|
59075
|
+
bold: true,
|
|
59076
|
+
children: "Error"
|
|
59077
|
+
}, undefined, false, undefined, this),
|
|
59075
59078
|
/* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Box_default, {
|
|
59076
|
-
|
|
59077
|
-
borderColor: "#ef4444",
|
|
59078
|
-
padding: 2,
|
|
59079
|
-
marginBottom: 1,
|
|
59080
|
-
children: [
|
|
59081
|
-
/* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Text, {
|
|
59082
|
-
color: "#ef4444",
|
|
59083
|
-
bold: true,
|
|
59084
|
-
children: "Error"
|
|
59085
|
-
}, undefined, false, undefined, this),
|
|
59086
|
-
/* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Box_default, {
|
|
59087
|
-
marginTop: 1,
|
|
59088
|
-
children: /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Text, {
|
|
59089
|
-
color: "#e5e7eb",
|
|
59090
|
-
children: state.error
|
|
59091
|
-
}, undefined, false, undefined, this)
|
|
59092
|
-
}, undefined, false, undefined, this)
|
|
59093
|
-
]
|
|
59094
|
-
}, undefined, true, undefined, this),
|
|
59095
|
-
/* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Box_default, {
|
|
59079
|
+
marginTop: 1,
|
|
59096
59080
|
children: /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Text, {
|
|
59097
|
-
color: "#
|
|
59098
|
-
children:
|
|
59099
|
-
|
|
59100
|
-
/* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Text, {
|
|
59101
|
-
color: "#ef4444",
|
|
59102
|
-
children: "Esc"
|
|
59103
|
-
}, undefined, false, undefined, this),
|
|
59104
|
-
" or",
|
|
59105
|
-
" ",
|
|
59106
|
-
/* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Text, {
|
|
59107
|
-
color: "#ef4444",
|
|
59108
|
-
children: "Ctrl+C"
|
|
59109
|
-
}, undefined, false, undefined, this),
|
|
59110
|
-
" to exit"
|
|
59111
|
-
]
|
|
59112
|
-
}, undefined, true, undefined, this)
|
|
59081
|
+
color: "#e5e7eb",
|
|
59082
|
+
children: state.error
|
|
59083
|
+
}, undefined, false, undefined, this)
|
|
59113
59084
|
}, undefined, false, undefined, this)
|
|
59114
59085
|
]
|
|
59115
59086
|
}, undefined, true, undefined, this);
|
|
@@ -59171,7 +59142,7 @@ var BetterCommitApp = ({
|
|
|
59171
59142
|
/* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Text, {
|
|
59172
59143
|
bold: true,
|
|
59173
59144
|
color: "#8b5cf6",
|
|
59174
|
-
children: "Better
|
|
59145
|
+
children: "Better-Commit"
|
|
59175
59146
|
}, undefined, false, undefined, this),
|
|
59176
59147
|
/* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Box_default, {
|
|
59177
59148
|
marginTop: 1,
|
|
@@ -59193,7 +59164,7 @@ var BetterCommitApp = ({
|
|
|
59193
59164
|
/* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Text, {
|
|
59194
59165
|
bold: true,
|
|
59195
59166
|
color: "#8b5cf6",
|
|
59196
|
-
children: "Better
|
|
59167
|
+
children: "Better-Commit"
|
|
59197
59168
|
}, undefined, false, undefined, this),
|
|
59198
59169
|
/* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Text, {
|
|
59199
59170
|
color: "#6b7280",
|
|
@@ -59477,7 +59448,18 @@ var ConfigApp = ({ onExit }) => {
|
|
|
59477
59448
|
const [config, setConfig] = import_react26.useState(configManager.getConfig());
|
|
59478
59449
|
const [activeDialog, setActiveDialog] = import_react26.useState(null);
|
|
59479
59450
|
const [menuIndex, setMenuIndex] = import_react26.useState(0);
|
|
59480
|
-
const
|
|
59451
|
+
const modelOptions = import_react26.useMemo(() => [
|
|
59452
|
+
{
|
|
59453
|
+
display: "llama-3.1-8b-instant (fastest)",
|
|
59454
|
+
value: "llama-3.1-8b-instant"
|
|
59455
|
+
},
|
|
59456
|
+
{
|
|
59457
|
+
display: "llama-3.3-70b-versatile (most capable)",
|
|
59458
|
+
value: "llama-3.3-70b-versatile"
|
|
59459
|
+
},
|
|
59460
|
+
{ display: "openai/gpt-oss-20b (balanced)", value: "openai/gpt-oss-20b" }
|
|
59461
|
+
], []);
|
|
59462
|
+
const menuItems = import_react26.useMemo(() => [
|
|
59481
59463
|
{
|
|
59482
59464
|
key: "groqApiKey",
|
|
59483
59465
|
label: "Groq API Key",
|
|
@@ -59487,11 +59469,7 @@ var ConfigApp = ({ onExit }) => {
|
|
|
59487
59469
|
key: "model",
|
|
59488
59470
|
label: "AI Model",
|
|
59489
59471
|
type: "select",
|
|
59490
|
-
options:
|
|
59491
|
-
"llama-3.1-8b-instant",
|
|
59492
|
-
"llama-3.3-70b-versatile",
|
|
59493
|
-
"openai/gpt-oss-20b"
|
|
59494
|
-
]
|
|
59472
|
+
options: modelOptions.map((m2) => m2.display)
|
|
59495
59473
|
},
|
|
59496
59474
|
{
|
|
59497
59475
|
key: "commitStyle",
|
|
@@ -59504,8 +59482,8 @@ var ConfigApp = ({ onExit }) => {
|
|
|
59504
59482
|
label: "Custom Prompt",
|
|
59505
59483
|
type: "textarea"
|
|
59506
59484
|
}
|
|
59507
|
-
];
|
|
59508
|
-
const saveAndExit = () => {
|
|
59485
|
+
], [modelOptions]);
|
|
59486
|
+
const saveAndExit = import_react26.useCallback(() => {
|
|
59509
59487
|
const finalConfig = {
|
|
59510
59488
|
...config,
|
|
59511
59489
|
maxHistoryCommits: 40,
|
|
@@ -59514,11 +59492,11 @@ var ConfigApp = ({ onExit }) => {
|
|
|
59514
59492
|
configManager.updateConfig(finalConfig);
|
|
59515
59493
|
onExit("Configuration saved");
|
|
59516
59494
|
exit();
|
|
59517
|
-
};
|
|
59518
|
-
const cancelAndExit = () => {
|
|
59495
|
+
}, [config, onExit, exit]);
|
|
59496
|
+
const cancelAndExit = import_react26.useCallback(() => {
|
|
59519
59497
|
onExit("Configuration cancelled");
|
|
59520
59498
|
exit();
|
|
59521
|
-
};
|
|
59499
|
+
}, [onExit, exit]);
|
|
59522
59500
|
const handleInput = import_react26.useCallback((input, key) => {
|
|
59523
59501
|
if (activeDialog)
|
|
59524
59502
|
return;
|
|
@@ -59543,11 +59521,18 @@ var ConfigApp = ({ onExit }) => {
|
|
|
59543
59521
|
} else if (key.escape || key.ctrl && input === "c") {
|
|
59544
59522
|
cancelAndExit();
|
|
59545
59523
|
}
|
|
59546
|
-
}, [activeDialog, menuIndex, menuItems]);
|
|
59524
|
+
}, [activeDialog, menuIndex, menuItems, saveAndExit, cancelAndExit]);
|
|
59547
59525
|
use_input_default(handleInput, { isActive: !activeDialog });
|
|
59548
59526
|
const handleDialogSubmit = (value) => {
|
|
59549
59527
|
if (activeDialog) {
|
|
59550
|
-
|
|
59528
|
+
let finalValue = value;
|
|
59529
|
+
if (activeDialog.key === "model") {
|
|
59530
|
+
const modelOption = modelOptions.find((m2) => m2.display === value);
|
|
59531
|
+
if (modelOption) {
|
|
59532
|
+
finalValue = modelOption.value;
|
|
59533
|
+
}
|
|
59534
|
+
}
|
|
59535
|
+
setConfig((prev) => ({ ...prev, [activeDialog.key]: finalValue }));
|
|
59551
59536
|
setActiveDialog(null);
|
|
59552
59537
|
}
|
|
59553
59538
|
};
|
|
@@ -59573,6 +59558,15 @@ var ConfigApp = ({ onExit }) => {
|
|
|
59573
59558
|
backgroundColor: bgColor,
|
|
59574
59559
|
children: "••••••••"
|
|
59575
59560
|
}, undefined, false, undefined, this);
|
|
59561
|
+
if (key === "model") {
|
|
59562
|
+
const modelOption = modelOptions.find((m2) => m2.value === val);
|
|
59563
|
+
const displayValue = modelOption ? modelOption.display : val;
|
|
59564
|
+
return /* @__PURE__ */ jsx_dev_runtime6.jsxDEV(Text, {
|
|
59565
|
+
color: textColor,
|
|
59566
|
+
backgroundColor: bgColor,
|
|
59567
|
+
children: displayValue
|
|
59568
|
+
}, undefined, false, undefined, this);
|
|
59569
|
+
}
|
|
59576
59570
|
if (key === "customPrompt")
|
|
59577
59571
|
return /* @__PURE__ */ jsx_dev_runtime6.jsxDEV(Text, {
|
|
59578
59572
|
color: textColor,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sncommit",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "AI-powered git commit message generator with beautiful TUI",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -8,6 +8,9 @@
|
|
|
8
8
|
"better-commit": "dist/index.js",
|
|
9
9
|
"bc": "dist/index.js"
|
|
10
10
|
},
|
|
11
|
+
"files": [
|
|
12
|
+
"dist"
|
|
13
|
+
],
|
|
11
14
|
"scripts": {
|
|
12
15
|
"build": "bun build src/index.tsx --outdir dist --target node",
|
|
13
16
|
"dev": "bun run build && bun run start",
|
|
@@ -53,4 +56,4 @@
|
|
|
53
56
|
"node": ">=18.0.0",
|
|
54
57
|
"bun": ">=1.0.0"
|
|
55
58
|
}
|
|
56
|
-
}
|
|
59
|
+
}
|