add gcpropose helper command
This commit is contained in:
parent
f86b8085c2
commit
effa01310b
5 changed files with 210 additions and 35 deletions
|
|
@ -22,6 +22,7 @@ with lib;
|
|||
bx = "branchdel";
|
||||
b = "branch";
|
||||
bs = "branching_setup";
|
||||
gcp = "gcpropose";
|
||||
};
|
||||
|
||||
environment.shellInit = lib.concatStringsSep "\n\n" [
|
||||
|
|
@ -30,5 +31,6 @@ with lib;
|
|||
(builtins.readFile ./branchd.func.sh)
|
||||
(builtins.readFile ./link_ignored.func.sh)
|
||||
(builtins.readFile ./branching_setup.func.sh)
|
||||
(builtins.readFile ./gcpropose.func.sh)
|
||||
];
|
||||
}
|
||||
|
|
|
|||
177
flakes/common/nix_modules/git/gcpropose.func.sh
Normal file
177
flakes/common/nix_modules/git/gcpropose.func.sh
Normal file
|
|
@ -0,0 +1,177 @@
|
|||
gcpropose() {
|
||||
local LITELLM_BASE_URL="http://h001.net.joshuabell.xyz:8094"
|
||||
local LITELLM_MODEL="azure-gpt-5-mini-2025-08-07"
|
||||
|
||||
local mode="staged"
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
-a) mode="all"; shift ;;
|
||||
-h|--help)
|
||||
cat <<EOF
|
||||
Usage: gcpropose [-a]
|
||||
|
||||
Propose a short git commit subject line using a LiteLLM model.
|
||||
|
||||
Defaults:
|
||||
- without -a: uses staged diff (git diff --staged)
|
||||
- with -a : uses full diff vs HEAD (git diff HEAD)
|
||||
EOF
|
||||
return 0
|
||||
;;
|
||||
*)
|
||||
echo "Unknown arg: $1" >&2
|
||||
return 2
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
||||
echo "Not inside a git repository." >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! command -v curl >/dev/null 2>&1; then
|
||||
echo "Missing dependency: curl" >&2
|
||||
return 1
|
||||
fi
|
||||
if ! command -v jq >/dev/null 2>&1; then
|
||||
echo "Missing dependency: jq" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
local diff
|
||||
if [ "$mode" = "all" ]; then
|
||||
diff=$(git diff HEAD)
|
||||
else
|
||||
diff=$(git diff --staged)
|
||||
fi
|
||||
|
||||
if [ -z "$diff" ]; then
|
||||
if [ "$mode" = "all" ]; then
|
||||
echo "No changes vs HEAD." >&2
|
||||
else
|
||||
echo "No staged changes." >&2
|
||||
fi
|
||||
return 1
|
||||
fi
|
||||
|
||||
local git_status
|
||||
git_status=$(git status --porcelain=v1 2>/dev/null || true)
|
||||
|
||||
local max_chars=10000
|
||||
diff=$(printf "%s" "$diff" | head -c "$max_chars")
|
||||
|
||||
local name_status
|
||||
if [ "$mode" = "all" ]; then
|
||||
name_status=$(git diff --name-status HEAD 2>/dev/null || true)
|
||||
else
|
||||
name_status=$(git diff --name-status --staged 2>/dev/null || true)
|
||||
fi
|
||||
|
||||
local prompt
|
||||
prompt=$(cat <<EOF
|
||||
Propose a concise git commit subject line based on the changes.
|
||||
|
||||
Rules:
|
||||
- Output ONLY the commit subject line.
|
||||
- Imperative mood.
|
||||
- Max 72 characters.
|
||||
- No quotes, no backticks, no trailing period.
|
||||
|
||||
git status --porcelain:
|
||||
${git_status}
|
||||
|
||||
files changed:
|
||||
${name_status}
|
||||
|
||||
git diff (truncated):
|
||||
${diff}
|
||||
EOF
|
||||
)
|
||||
|
||||
local payload_chat
|
||||
payload_chat=$(jq -n \
|
||||
--arg model "$LITELLM_MODEL" \
|
||||
--arg content "$prompt" \
|
||||
'{
|
||||
model: $model,
|
||||
messages: [
|
||||
{
|
||||
role: "system",
|
||||
content: "You write excellent, conventional git commit subject lines."
|
||||
},
|
||||
{
|
||||
role: "user",
|
||||
content: $content
|
||||
}
|
||||
],
|
||||
temperature: 0.2
|
||||
}')
|
||||
|
||||
local curl_out http_code body
|
||||
curl_out=$(curl -sS -w "\n%{http_code}" \
|
||||
-X POST "${LITELLM_BASE_URL}/v1/chat/completions" \
|
||||
-H "Content-Type: application/json" \
|
||||
${LITELLM_API_KEY:+-H "Authorization: Bearer ${LITELLM_API_KEY}"} \
|
||||
-d "$payload_chat") || return 1
|
||||
|
||||
http_code=$(printf "%s" "$curl_out" | tail -n 1)
|
||||
body=$(printf "%s" "$curl_out" | sed '$d')
|
||||
|
||||
if [ "$http_code" -lt 200 ] || [ "$http_code" -ge 300 ]; then
|
||||
echo "LiteLLM request failed (HTTP $http_code)." >&2
|
||||
printf "%s\n" "$body" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
local message
|
||||
message=$(printf "%s" "$body" | jq -r '
|
||||
.choices[0].message.content
|
||||
| if type == "string" then .
|
||||
elif type == "array" then (map(select(.type=="text") | .text) | join(""))
|
||||
else ""
|
||||
end
|
||||
' 2>/dev/null || true)
|
||||
message=$(printf "%s" "$message" | sed -n '1p' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
|
||||
|
||||
if [ -n "$message" ] && [ "$message" != "null" ]; then
|
||||
printf "%s\n" "$message"
|
||||
return 0
|
||||
fi
|
||||
|
||||
local payload_responses
|
||||
payload_responses=$(jq -n \
|
||||
--arg model "$LITELLM_MODEL" \
|
||||
--arg input "$prompt" \
|
||||
'{
|
||||
model: $model,
|
||||
input: $input,
|
||||
max_output_tokens: 64
|
||||
}')
|
||||
|
||||
curl_out=$(curl -sS -w "\n%{http_code}" \
|
||||
-X POST "${LITELLM_BASE_URL}/v1/responses" \
|
||||
-H "Content-Type: application/json" \
|
||||
${LITELLM_API_KEY:+-H "Authorization: Bearer ${LITELLM_API_KEY}"} \
|
||||
-d "$payload_responses") || return 1
|
||||
|
||||
http_code=$(printf "%s" "$curl_out" | tail -n 1)
|
||||
body=$(printf "%s" "$curl_out" | sed '$d')
|
||||
|
||||
if [ "$http_code" -lt 200 ] || [ "$http_code" -ge 300 ]; then
|
||||
echo "LiteLLM request failed (HTTP $http_code)." >&2
|
||||
printf "%s\n" "$body" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
message=$(printf "%s" "$body" | jq -r '(.output_text // empty)' 2>/dev/null || true)
|
||||
message=$(printf "%s" "$message" | sed -n '1p' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
|
||||
|
||||
if [ -z "$message" ] || [ "$message" = "null" ]; then
|
||||
echo "Failed to parse model response." >&2
|
||||
printf "%s\n" "$body" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
printf "%s\n" "$message"
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue