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";
|
controlMaster = "no";
|
||||||
controlPath = "~/.ssh/master-%r@%n:%p";
|
controlPath = "~/.ssh/master-%r@%n:%p";
|
||||||
controlPersist = "no";
|
controlPersist = "no";
|
||||||
|
extraOptions = {
|
||||||
StrictHostKeyChecking = "accept-new";
|
StrictHostKeyChecking = "accept-new";
|
||||||
};
|
};
|
||||||
|
};
|
||||||
|
|
||||||
# EXTERNAL
|
# EXTERNAL
|
||||||
"github.com" = lib.mkIf (hasSecret "nix2github") {
|
"github.com" = lib.mkIf (hasSecret "nix2github") {
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ with lib;
|
||||||
bx = "branchdel";
|
bx = "branchdel";
|
||||||
b = "branch";
|
b = "branch";
|
||||||
bs = "branching_setup";
|
bs = "branching_setup";
|
||||||
|
gcp = "gcpropose";
|
||||||
};
|
};
|
||||||
|
|
||||||
environment.shellInit = lib.concatStringsSep "\n\n" [
|
environment.shellInit = lib.concatStringsSep "\n\n" [
|
||||||
|
|
@ -30,5 +31,6 @@ with lib;
|
||||||
(builtins.readFile ./branchd.func.sh)
|
(builtins.readFile ./branchd.func.sh)
|
||||||
(builtins.readFile ./link_ignored.func.sh)
|
(builtins.readFile ./link_ignored.func.sh)
|
||||||
(builtins.readFile ./branching_setup.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": {
|
"locked": {
|
||||||
"dir": "flakes/beszel",
|
"dir": "flakes/beszel",
|
||||||
"lastModified": 1767572382,
|
"lastModified": 1767575724,
|
||||||
"narHash": "sha256-oDoVrmMpww4uY3Ez1XzrHsxJTZmBMiOO/mNrU2njiWQ=",
|
"narHash": "sha256-L+3hoO4t3RCXkp9RXyXpJlCkzj6AdTOsstUv7RphEBM=",
|
||||||
"ref": "refs/heads/master",
|
"ref": "refs/heads/master",
|
||||||
"rev": "165f87ebc19a48b56008738d01c5e2e5bebbbdfd",
|
"rev": "f86b8085c2ad39986c194b28d51260f8f402572a",
|
||||||
"revCount": 1038,
|
"revCount": 1041,
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://git.joshuabell.xyz/ringofstorms/dotfiles"
|
"url": "https://git.joshuabell.xyz/ringofstorms/dotfiles"
|
||||||
},
|
},
|
||||||
|
|
@ -63,20 +63,14 @@
|
||||||
},
|
},
|
||||||
"common": {
|
"common": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"dir": "flakes/common",
|
"path": "../../flakes/common",
|
||||||
"lastModified": 1767572382,
|
"type": "path"
|
||||||
"narHash": "sha256-oDoVrmMpww4uY3Ez1XzrHsxJTZmBMiOO/mNrU2njiWQ=",
|
|
||||||
"ref": "refs/heads/master",
|
|
||||||
"rev": "165f87ebc19a48b56008738d01c5e2e5bebbbdfd",
|
|
||||||
"revCount": 1038,
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://git.joshuabell.xyz/ringofstorms/dotfiles"
|
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"dir": "flakes/common",
|
"path": "../../flakes/common",
|
||||||
"type": "git",
|
"type": "path"
|
||||||
"url": "https://git.joshuabell.xyz/ringofstorms/dotfiles"
|
},
|
||||||
}
|
"parent": []
|
||||||
},
|
},
|
||||||
"crane": {
|
"crane": {
|
||||||
"locked": {
|
"locked": {
|
||||||
|
|
@ -123,11 +117,11 @@
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"dir": "flakes/de_plasma",
|
"dir": "flakes/de_plasma",
|
||||||
"lastModified": 1767572382,
|
"lastModified": 1767575724,
|
||||||
"narHash": "sha256-oDoVrmMpww4uY3Ez1XzrHsxJTZmBMiOO/mNrU2njiWQ=",
|
"narHash": "sha256-L+3hoO4t3RCXkp9RXyXpJlCkzj6AdTOsstUv7RphEBM=",
|
||||||
"ref": "refs/heads/master",
|
"ref": "refs/heads/master",
|
||||||
"rev": "165f87ebc19a48b56008738d01c5e2e5bebbbdfd",
|
"rev": "f86b8085c2ad39986c194b28d51260f8f402572a",
|
||||||
"revCount": 1038,
|
"revCount": 1041,
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://git.joshuabell.xyz/ringofstorms/dotfiles"
|
"url": "https://git.joshuabell.xyz/ringofstorms/dotfiles"
|
||||||
},
|
},
|
||||||
|
|
@ -161,11 +155,11 @@
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"dir": "flakes/flatpaks",
|
"dir": "flakes/flatpaks",
|
||||||
"lastModified": 1767572382,
|
"lastModified": 1767575724,
|
||||||
"narHash": "sha256-oDoVrmMpww4uY3Ez1XzrHsxJTZmBMiOO/mNrU2njiWQ=",
|
"narHash": "sha256-L+3hoO4t3RCXkp9RXyXpJlCkzj6AdTOsstUv7RphEBM=",
|
||||||
"ref": "refs/heads/master",
|
"ref": "refs/heads/master",
|
||||||
"rev": "165f87ebc19a48b56008738d01c5e2e5bebbbdfd",
|
"rev": "f86b8085c2ad39986c194b28d51260f8f402572a",
|
||||||
"revCount": 1038,
|
"revCount": 1041,
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://git.joshuabell.xyz/ringofstorms/dotfiles"
|
"url": "https://git.joshuabell.xyz/ringofstorms/dotfiles"
|
||||||
},
|
},
|
||||||
|
|
@ -1237,11 +1231,11 @@
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"dir": "flakes/opencode",
|
"dir": "flakes/opencode",
|
||||||
"lastModified": 1767572382,
|
"lastModified": 1767575724,
|
||||||
"narHash": "sha256-oDoVrmMpww4uY3Ez1XzrHsxJTZmBMiOO/mNrU2njiWQ=",
|
"narHash": "sha256-L+3hoO4t3RCXkp9RXyXpJlCkzj6AdTOsstUv7RphEBM=",
|
||||||
"ref": "refs/heads/master",
|
"ref": "refs/heads/master",
|
||||||
"rev": "165f87ebc19a48b56008738d01c5e2e5bebbbdfd",
|
"rev": "f86b8085c2ad39986c194b28d51260f8f402572a",
|
||||||
"revCount": 1038,
|
"revCount": 1041,
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://git.joshuabell.xyz/ringofstorms/dotfiles"
|
"url": "https://git.joshuabell.xyz/ringofstorms/dotfiles"
|
||||||
},
|
},
|
||||||
|
|
@ -1446,11 +1440,11 @@
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"dir": "flakes/secrets",
|
"dir": "flakes/secrets",
|
||||||
"lastModified": 1767572382,
|
"lastModified": 1767575724,
|
||||||
"narHash": "sha256-oDoVrmMpww4uY3Ez1XzrHsxJTZmBMiOO/mNrU2njiWQ=",
|
"narHash": "sha256-L+3hoO4t3RCXkp9RXyXpJlCkzj6AdTOsstUv7RphEBM=",
|
||||||
"ref": "refs/heads/master",
|
"ref": "refs/heads/master",
|
||||||
"rev": "165f87ebc19a48b56008738d01c5e2e5bebbbdfd",
|
"rev": "f86b8085c2ad39986c194b28d51260f8f402572a",
|
||||||
"revCount": 1038,
|
"revCount": 1041,
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://git.joshuabell.xyz/ringofstorms/dotfiles"
|
"url": "https://git.joshuabell.xyz/ringofstorms/dotfiles"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,8 @@
|
||||||
nixpkgs-unstable.url = "github:nixos/nixpkgs/nixos-unstable";
|
nixpkgs-unstable.url = "github:nixos/nixpkgs/nixos-unstable";
|
||||||
|
|
||||||
# Use relative to get current version for testin
|
# Use relative to get current version for testin
|
||||||
# common.url = "path:../../flakes/common";
|
common.url = "path:../../flakes/common";
|
||||||
common.url = "git+https://git.joshuabell.xyz/ringofstorms/dotfiles?dir=flakes/common";
|
# common.url = "git+https://git.joshuabell.xyz/ringofstorms/dotfiles?dir=flakes/common";
|
||||||
# secrets.url = "path:../../flakes/secrets";
|
# secrets.url = "path:../../flakes/secrets";
|
||||||
secrets.url = "git+https://git.joshuabell.xyz/ringofstorms/dotfiles?dir=flakes/secrets";
|
secrets.url = "git+https://git.joshuabell.xyz/ringofstorms/dotfiles?dir=flakes/secrets";
|
||||||
# flatpaks.url = "path:../../flakes/flatpaks";
|
# flatpaks.url = "path:../../flakes/flatpaks";
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue