add gcpropose helper command
This commit is contained in:
parent
f86b8085c2
commit
effa01310b
5 changed files with 210 additions and 35 deletions
|
|
@ -33,8 +33,10 @@ in
|
|||
controlMaster = "no";
|
||||
controlPath = "~/.ssh/master-%r@%n:%p";
|
||||
controlPersist = "no";
|
||||
extraOptions = {
|
||||
StrictHostKeyChecking = "accept-new";
|
||||
};
|
||||
};
|
||||
|
||||
# EXTERNAL
|
||||
"github.com" = lib.mkIf (hasSecret "nix2github") {
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
}
|
||||
58
hosts/oren/flake.lock
generated
58
hosts/oren/flake.lock
generated
|
|
@ -31,11 +31,11 @@
|
|||
},
|
||||
"locked": {
|
||||
"dir": "flakes/beszel",
|
||||
"lastModified": 1767572382,
|
||||
"narHash": "sha256-oDoVrmMpww4uY3Ez1XzrHsxJTZmBMiOO/mNrU2njiWQ=",
|
||||
"lastModified": 1767575724,
|
||||
"narHash": "sha256-L+3hoO4t3RCXkp9RXyXpJlCkzj6AdTOsstUv7RphEBM=",
|
||||
"ref": "refs/heads/master",
|
||||
"rev": "165f87ebc19a48b56008738d01c5e2e5bebbbdfd",
|
||||
"revCount": 1038,
|
||||
"rev": "f86b8085c2ad39986c194b28d51260f8f402572a",
|
||||
"revCount": 1041,
|
||||
"type": "git",
|
||||
"url": "https://git.joshuabell.xyz/ringofstorms/dotfiles"
|
||||
},
|
||||
|
|
@ -63,20 +63,14 @@
|
|||
},
|
||||
"common": {
|
||||
"locked": {
|
||||
"dir": "flakes/common",
|
||||
"lastModified": 1767572382,
|
||||
"narHash": "sha256-oDoVrmMpww4uY3Ez1XzrHsxJTZmBMiOO/mNrU2njiWQ=",
|
||||
"ref": "refs/heads/master",
|
||||
"rev": "165f87ebc19a48b56008738d01c5e2e5bebbbdfd",
|
||||
"revCount": 1038,
|
||||
"type": "git",
|
||||
"url": "https://git.joshuabell.xyz/ringofstorms/dotfiles"
|
||||
"path": "../../flakes/common",
|
||||
"type": "path"
|
||||
},
|
||||
"original": {
|
||||
"dir": "flakes/common",
|
||||
"type": "git",
|
||||
"url": "https://git.joshuabell.xyz/ringofstorms/dotfiles"
|
||||
}
|
||||
"path": "../../flakes/common",
|
||||
"type": "path"
|
||||
},
|
||||
"parent": []
|
||||
},
|
||||
"crane": {
|
||||
"locked": {
|
||||
|
|
@ -123,11 +117,11 @@
|
|||
},
|
||||
"locked": {
|
||||
"dir": "flakes/de_plasma",
|
||||
"lastModified": 1767572382,
|
||||
"narHash": "sha256-oDoVrmMpww4uY3Ez1XzrHsxJTZmBMiOO/mNrU2njiWQ=",
|
||||
"lastModified": 1767575724,
|
||||
"narHash": "sha256-L+3hoO4t3RCXkp9RXyXpJlCkzj6AdTOsstUv7RphEBM=",
|
||||
"ref": "refs/heads/master",
|
||||
"rev": "165f87ebc19a48b56008738d01c5e2e5bebbbdfd",
|
||||
"revCount": 1038,
|
||||
"rev": "f86b8085c2ad39986c194b28d51260f8f402572a",
|
||||
"revCount": 1041,
|
||||
"type": "git",
|
||||
"url": "https://git.joshuabell.xyz/ringofstorms/dotfiles"
|
||||
},
|
||||
|
|
@ -161,11 +155,11 @@
|
|||
},
|
||||
"locked": {
|
||||
"dir": "flakes/flatpaks",
|
||||
"lastModified": 1767572382,
|
||||
"narHash": "sha256-oDoVrmMpww4uY3Ez1XzrHsxJTZmBMiOO/mNrU2njiWQ=",
|
||||
"lastModified": 1767575724,
|
||||
"narHash": "sha256-L+3hoO4t3RCXkp9RXyXpJlCkzj6AdTOsstUv7RphEBM=",
|
||||
"ref": "refs/heads/master",
|
||||
"rev": "165f87ebc19a48b56008738d01c5e2e5bebbbdfd",
|
||||
"revCount": 1038,
|
||||
"rev": "f86b8085c2ad39986c194b28d51260f8f402572a",
|
||||
"revCount": 1041,
|
||||
"type": "git",
|
||||
"url": "https://git.joshuabell.xyz/ringofstorms/dotfiles"
|
||||
},
|
||||
|
|
@ -1237,11 +1231,11 @@
|
|||
},
|
||||
"locked": {
|
||||
"dir": "flakes/opencode",
|
||||
"lastModified": 1767572382,
|
||||
"narHash": "sha256-oDoVrmMpww4uY3Ez1XzrHsxJTZmBMiOO/mNrU2njiWQ=",
|
||||
"lastModified": 1767575724,
|
||||
"narHash": "sha256-L+3hoO4t3RCXkp9RXyXpJlCkzj6AdTOsstUv7RphEBM=",
|
||||
"ref": "refs/heads/master",
|
||||
"rev": "165f87ebc19a48b56008738d01c5e2e5bebbbdfd",
|
||||
"revCount": 1038,
|
||||
"rev": "f86b8085c2ad39986c194b28d51260f8f402572a",
|
||||
"revCount": 1041,
|
||||
"type": "git",
|
||||
"url": "https://git.joshuabell.xyz/ringofstorms/dotfiles"
|
||||
},
|
||||
|
|
@ -1446,11 +1440,11 @@
|
|||
},
|
||||
"locked": {
|
||||
"dir": "flakes/secrets",
|
||||
"lastModified": 1767572382,
|
||||
"narHash": "sha256-oDoVrmMpww4uY3Ez1XzrHsxJTZmBMiOO/mNrU2njiWQ=",
|
||||
"lastModified": 1767575724,
|
||||
"narHash": "sha256-L+3hoO4t3RCXkp9RXyXpJlCkzj6AdTOsstUv7RphEBM=",
|
||||
"ref": "refs/heads/master",
|
||||
"rev": "165f87ebc19a48b56008738d01c5e2e5bebbbdfd",
|
||||
"revCount": 1038,
|
||||
"rev": "f86b8085c2ad39986c194b28d51260f8f402572a",
|
||||
"revCount": 1041,
|
||||
"type": "git",
|
||||
"url": "https://git.joshuabell.xyz/ringofstorms/dotfiles"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@
|
|||
nixpkgs-unstable.url = "github:nixos/nixpkgs/nixos-unstable";
|
||||
|
||||
# Use relative to get current version for testin
|
||||
# common.url = "path:../../flakes/common";
|
||||
common.url = "git+https://git.joshuabell.xyz/ringofstorms/dotfiles?dir=flakes/common";
|
||||
common.url = "path:../../flakes/common";
|
||||
# common.url = "git+https://git.joshuabell.xyz/ringofstorms/dotfiles?dir=flakes/common";
|
||||
# secrets.url = "path:../../flakes/secrets";
|
||||
secrets.url = "git+https://git.joshuabell.xyz/ringofstorms/dotfiles?dir=flakes/secrets";
|
||||
# flatpaks.url = "path:../../flakes/flatpaks";
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue