Skip to content

Commit 4c27a81

Browse files
committed
feat(lazygit): add AI commit message generation with lazycommit
Add new lazycommit script that generates Conventional Commit messages using OpenRouter API. Includes configuration for API key and model selection. Also add crush to shared packages and update gitignore with .crush pattern. The lazygit configuration now has a custom command (Ctrl+A) to trigger commit message generation from staged changes.
1 parent fa85b3a commit 4c27a81

5 files changed

Lines changed: 241 additions & 1 deletion

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
.sops.yaml
2+
.crush

home/programs/lazygit/default.nix

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Lazygit is a simple terminal UI for git commands.
2-
{ config, lib, ... }:
2+
{ config, lib, pkgs, ... }:
33
let
44
accent = "#${config.lib.stylix.colors.base0D}";
55
muted = "#${config.lib.stylix.colors.base03}";
@@ -28,6 +28,14 @@ in {
2828
showBottomLine = true;
2929
nerdFontsVersion = "3";
3030
};
31+
# For the very lazy, with this you can generate a commit message :)
32+
customCommands = [{
33+
key = "<c-a>";
34+
description = "Generate AI commit message";
35+
command = "lazycommit";
36+
context = "files";
37+
output = "terminal";
38+
}];
3139
};
3240
};
3341
}

home/scripts/default.nix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,6 @@
1212
./screenshot
1313
./system
1414
./openvpn
15+
./lazycommit
1516
];
1617
}
Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
{ pkgs, ... }:
2+
let
3+
lazycommit = pkgs.writeShellScriptBin "lazycommit"
4+
# bash
5+
''
6+
#!/usr/bin/env bash
7+
8+
# Configurable settings
9+
CONFIG_DIR="$HOME/.config/lazycommit"
10+
CONFIG_FILE="$CONFIG_DIR/api_key"
11+
MODEL_FILE="$CONFIG_DIR/model"
12+
13+
# OPENROUTER_MODEL="deepseek/deepseek-chat-v3-0324:free"
14+
# OPENROUTER_MODEL="meta-llama/llama-3.3-70b-instruct:free"
15+
OPENROUTER_MODEL="tngtech/deepseek-r1t-chimera:free"
16+
# OPENROUTER_MODEL="google/gemini-flash-1.5-8b"
17+
OPENROUTER_TEMPERATURE="0.7"
18+
19+
# Function to save API key
20+
save_api_key() {
21+
mkdir -p "$CONFIG_DIR"
22+
echo "$1" > "$CONFIG_FILE"
23+
chmod 600 "$CONFIG_FILE"
24+
echo "✅ API key saved to $CONFIG_FILE"
25+
}
26+
27+
get_api_key() {
28+
# First try environment variable
29+
if [[ -n "$OPENROUTER_API_KEY" ]]; then
30+
echo "$OPENROUTER_API_KEY"
31+
# Save to config for future use
32+
save_api_key "$OPENROUTER_API_KEY"
33+
return 0
34+
fi
35+
36+
# Then try config file
37+
if [[ -f "$CONFIG_FILE" ]]; then
38+
API_KEY_FROM_FILE=$(cat "$CONFIG_FILE" | tr -d '[:space:]')
39+
if [[ -n "$API_KEY_FROM_FILE" ]]; then
40+
echo "$API_KEY_FROM_FILE" # This echoes the actual key
41+
return 0
42+
fi
43+
fi
44+
45+
# No API key found
46+
echo "" # Echo empty string instead of just returning failure
47+
return 1
48+
}
49+
50+
# Function to save model
51+
save_model() {
52+
mkdir -p "$CONFIG_DIR"
53+
echo "$1" > "$MODEL_FILE"
54+
chmod 600 "$MODEL_FILE"
55+
echo "✅ Model saved to $MODEL_FILE"
56+
}
57+
58+
# Function to get model
59+
get_model() {
60+
if [[ -f "$MODEL_FILE" ]]; then
61+
cat "$MODEL_FILE"
62+
else
63+
echo "$OPENROUTER_MODEL"
64+
fi
65+
}
66+
67+
# Parse command line arguments
68+
while [[ $# -gt 0 ]]; do
69+
case $1 in
70+
--api-key)
71+
if [[ -n "$2" && "$2" != -* ]]; then
72+
save_api_key "$2"
73+
shift 2
74+
else
75+
echo "❌ --api-key requires a valid API key"
76+
exit 1
77+
fi
78+
;;
79+
--model)
80+
if [[ -n "$2" && "$2" != -* ]]; then
81+
save_model "$2"
82+
shift 2
83+
else
84+
echo "❌ --model requires a valid model name"
85+
exit 1
86+
fi
87+
;;
88+
-h|--help)
89+
echo "Usage: lazycommit [options]"
90+
echo ""
91+
echo "Options:"
92+
echo " --api-key <key> Set api key"
93+
echo " --model <model> Set model (default: tngtech/deepseek-r1t-chimera:free)"
94+
echo " -h, --help Show this help message"
95+
echo ""
96+
echo "The API key can also be set via OPENROUTER_API_KEY environment variable."
97+
exit 0
98+
;;
99+
*)
100+
echo "❌ Unknown argument: $1"
101+
exit 1
102+
;;
103+
esac
104+
done
105+
106+
# Get API key
107+
API_KEY=$(get_api_key)
108+
if [[ -z "$API_KEY" ]]; then # Check if the key is empty
109+
echo "❌ No OpenRouter API key found."
110+
echo " Set OPENROUTER_API_KEY environment variable"
111+
exit 1
112+
fi
113+
114+
# Get model
115+
MODEL=$(get_model)
116+
117+
# Check if we're in a git repository
118+
if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
119+
echo "❌ Not in a git repository"
120+
exit 1
121+
fi
122+
123+
# Check if there are any staged changes
124+
if ! git diff --cached --quiet >/dev/null 2>&1; then
125+
# Get the diff of staged changes
126+
DIFF=$(git diff --cached)
127+
CHANGED_FILES="$(git diff --cached --name-only | tr '\n' ',' | sed 's/,$//' | sed 's/,/, /g')"
128+
else
129+
echo "❌ No staged changes found."
130+
echo " Please stage your changes first with: git add <files>"
131+
exit 1
132+
fi
133+
134+
printf "\033[36m🤖 Generating commit message...\033[0m\n"
135+
printf "\033[90mUsing model: $MODEL\033[0m\n"
136+
137+
SYSTEM_MESSAGE="You are an expert at writing Conventional Commit messages. Follow these rules strictly:
138+
139+
FORMAT: <type>(<scope>): <subject>
140+
\n<BLANK LINE>
141+
\n<body>
142+
\n<BLANK LINE>
143+
\n<footer>
144+
145+
TYPES:
146+
- feat: New feature
147+
- fix: Bug fix
148+
- docs: Documentation changes
149+
- style: Formatting, missing semicolons, etc (no code change)
150+
- refactor: Code refactoring (no behavior change)
151+
- perf: Performance improvements
152+
- test: Adding or updating tests
153+
- chore: Build process, tooling, or auxiliary changes
154+
155+
RULES:
156+
- Use imperative mood: \"fix bug\" not \"fixed bug\"
157+
- Subject line must be 50 characters or less
158+
- Body should explain why the changes were made and what changed
159+
- Body should wrap at 72 characters
160+
- Breaking changes must start with BREAKING CHANGE: in footer
161+
162+
The following files have changed: ''${CHANGED_FILES}.
163+
164+
CRITICAL INSTRUCTIONS:
165+
- Generate ONLY ONE commit message that encapsulates ALL changes
166+
- Return ONLY the commit message and description, nothing else
167+
- No explanations, no apologies, no additional text
168+
- Format must be: subject line + blank line + body
169+
- Ensure there is exactly one blank line between subject and body
170+
171+
Analyze the git diff and generate a proper Conventional Commit message."
172+
173+
# Remove extra spaces and newlines (but preserve intentional line breaks for the format)
174+
SYSTEM_MESSAGE=$(echo "$SYSTEM_MESSAGE" | sed 's/ //g' | sed 's/^ *//;s/ *$//')
175+
176+
JSON_DATA=$(jq -n \
177+
--arg model "$OPENROUTER_MODEL" \
178+
--arg system_message "$SYSTEM_MESSAGE" \
179+
--arg user_content "$DIFF" \
180+
--arg temperature "$OPENROUTER_TEMPERATURE" \
181+
'{
182+
model: $model,
183+
messages: [
184+
{role: "system", content: $system_message},
185+
{role: "user", content: $user_content}
186+
],
187+
temperature: ($temperature | tonumber)
188+
}')
189+
190+
RESPONSE=$(curl -s -X POST "https://openrouter.ai/api/v1/chat/completions" \
191+
-H "Content-Type: application/json" \
192+
-H "Authorization: Bearer $API_KEY" \
193+
-d "$JSON_DATA")
194+
195+
rm -f "$JSON_FILE"
196+
197+
if [[ $CURL_EXIT_CODE -ne 0 ]]; then
198+
printf "\033[31m❌ API request failed with curl exit code: $CURL_EXIT_CODE\033[0m\n"
199+
echo "Response: $RESPONSE"
200+
exit 1
201+
fi
202+
203+
if echo "$RESPONSE" | jq -e '.error' >/dev/null 2>&1; then
204+
ERROR_MESSAGE=$(echo "$RESPONSE" | jq -r '.error.message // .error' 2>/dev/null)
205+
printf "\033[31m❌ OpenRouter API error: $ERROR_MESSAGE\033[0m\n"
206+
echo "Full response: $RESPONSE"
207+
exit 1
208+
fi
209+
210+
MESSAGE=$(echo "$RESPONSE" | jq -r '.choices[0].message.content // empty' | sed 's/^ *//;s/ *$//')
211+
if [[ -z "$MESSAGE" ]]; then
212+
printf "\033[31m❌ Failed to generate commit message\033[0m\n"
213+
echo "Response: $RESPONSE"
214+
exit 1
215+
fi
216+
217+
# Clean up the message (remove markdown code blocks if present)
218+
MESSAGE=$(echo "$MESSAGE" | sed 's/^```.*//' | sed 's/^```$//' | sed '/^$/d')
219+
220+
# printf "\033[32m✅ Generated commit message:\033[0m\n"
221+
# echo "---"
222+
# echo "$MESSAGE"
223+
# echo "---"
224+
225+
# Use git's built-in editor with the AI message pre-filled
226+
git commit -e -m "$MESSAGE"
227+
'';
228+
229+
in { home.packages = [ lazycommit ]; }

home/shared.nix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
lazydocker
3737
bruno # rest client
3838
bruno-cli # cli for bruno, needed for bruno.nvim
39+
crush
3940
# ranger # terminal file explorer
4041
# screenkey # shows keypresses on screen
4142
# textpieces # Manipulate texts

0 commit comments

Comments
 (0)