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 CHANGED
@@ -1,4 +1,4 @@
1
- # Better Commit
1
+ # Better-Commit
2
2
 
3
3
  AI-powered git commit message generator with a beautiful TUI (Terminal User Interface).
4
4
 
package/dist/index.js CHANGED
@@ -58323,7 +58323,15 @@ class GroqService {
58323
58323
  }
58324
58324
  return this.parseSuggestions(content);
58325
58325
  } catch (e2) {
58326
- console.error("Error generating suggestions:", e2);
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
- console.error("Error generating suggestions:", e2);
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
- return parsed.suggestions.map((s2) => ({
58514
- message: s2.message || "",
58515
- type: s2.type || this.extractType(s2.message || ""),
58516
- description: s2.description || s2.message || ""
58517
- })).slice(0, 4);
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, onExit, exit, write]);
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 generateSuggestions(stagedFiles);
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 generateSuggestions = async (stagedFiles) => {
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 --config" to set it up.',
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
- const suggestions = await groqService.generateCommitSuggestions(stagedFiles, diff2, recentCommits, diffStats);
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
- generateSuggestions(state.stagedFiles);
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 generateSuggestionsFromCustomInput(customInput.trim());
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
- flexDirection: "column",
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
- borderStyle: "single",
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: "#6b7280",
59098
- children: [
59099
- "Press ",
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 Commit"
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 Commit"
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 menuItems = [
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
- setConfig((prev) => ({ ...prev, [activeDialog.key]: value }));
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.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
+ }