Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion TODO.md

This file was deleted.

26 changes: 23 additions & 3 deletions profile/bashrc.d/00-core.bash
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,29 @@ alias cd..='cd ..'
alias ll='ls -al --color=auto'

function add_alias {
if [ $# -ne 2 ]; then
echo "usage: add_alias <name> <command>" >&2
return 1
fi

# shellcheck disable=SC2139
alias "$1"="$2"
complete -F _complete_alias "$1"
if declare -F _complete_alias >/dev/null 2>&1; then
complete -F _complete_alias "$1"
fi
}

jprofile_path_prepend() {
if [ $# -ne 1 ] || [ -z "$1" ]; then
echo "usage: jprofile_path_prepend <dir>" >&2
return 1
fi
[ -d "$1" ] || return 0

case ":$PATH:" in
*":$1:"*) ;;
*) export PATH="$1:$PATH" ;;
esac
}

# --- Prompt ---
Expand Down Expand Up @@ -94,7 +114,7 @@ fi

# --- fzf ---
if [ -f /usr/local/etc/.fzf.bash ]; then
export PATH="/usr/local/etc/fzf/bin/:$PATH"
jprofile_path_prepend /usr/local/etc/fzf/bin
if [ -z "$__JPROFILE_RELOADING_BASHRC" ]; then
# shellcheck source=/dev/null
source /usr/local/etc/.fzf.bash
Expand All @@ -115,4 +135,4 @@ if [ -f /usr/local/etc/.fzf.bash ]; then
fi

# --- PATH ---
export PATH="/opt/nvim-linux64/bin:$PATH"
jprofile_path_prepend /opt/nvim-linux64/bin
2 changes: 1 addition & 1 deletion profile/bashrc.d/10-tmux.bash
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ alias fts=fix-tmux-ssh

# --- Tmuxifier init ---
if [ -d /usr/local/etc/.tmuxifier ]; then
export PATH="/usr/local/etc/.tmuxifier/bin:$PATH"
jprofile_path_prepend /usr/local/etc/.tmuxifier/bin
export TMUXIFIER_LAYOUT_PATH=/usr/local/etc/tmuxifiers/
if [ -z "$__JPROFILE_RELOADING_BASHRC" ]; then
eval "$(tmuxifier init -)"
Expand Down
11 changes: 11 additions & 0 deletions profile/bashrc.d/20-git.bash
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,21 @@ add_alias sbrc 'source ~/.bashrc'
unalias gch 2>/dev/null
gch() {
if [ $# -eq 0 ]; then
if ! command -v fzf >/dev/null 2>&1; then
echo "gch: fzf is required when no branch argument is provided" >&2
echo "usage: gch <branch-or-commit>" >&2
return 1
fi

local local_branches remote_branches selected
local_branches=$(git branch --format='%(refname:short)' 2>/dev/null)
remote_branches=$(git branch --remote --format='%(refname:short)' 2>/dev/null | grep -v '/HEAD$')
selected=$(printf '%s\n%s\n' "$local_branches" "$remote_branches" | awk '!seen[$0]++' | fzf --height=40% --reverse --prompt="Checkout branch> ")
local fzf_status=$?
if [ $fzf_status -ne 0 ]; then
[ $fzf_status -eq 130 ] && return 0
return $fzf_status
fi
[ -z "$selected" ] && return 0
if [[ "$selected" == */* ]]; then
local branch_name="${selected#*/}"
Expand Down
58 changes: 49 additions & 9 deletions profile/bashrc.d/30-ai.bash
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,26 @@

genCommitMsg() {
local model="$1"
if [ -z "$model" ]; then
echo "genCommitMsg: missing model" >&2
return 1
fi

local repo_root
repo_root="$(git rev-parse --show-toplevel 2>/dev/null)" || {
echo "Not a git repository" >&2
return 1
}

if git diff --cached --quiet --exit-code; then
echo "genCommitMsg: no staged changes" >&2
return 1
fi

local tmp="$repo_root/.gen_commit_msg.$$.diff"
git diff --staged >"$tmp"

local pi_run
if [ -f "$repo_root/profile/pi-unleashed-safely.sh" ]; then
pi_run="$repo_root/profile/pi-unleashed-safely.sh --dev"
else
Expand All @@ -31,25 +42,54 @@ genCommitMsg() {
--model "$model" \
--thinking off \
"@$tmp" \
"Write a one-line commit message for the currently staged changes following the Conventional Commits standard. Output only the commit message, no backticks, no formatting, just the text." 2>/dev/null) || exit_status=$?
"Write a one-line commit message for the currently staged changes following the Conventional Commits standard. Output only the commit message, no backticks, no formatting, just the text." 2>&1) || exit_status=$?

rm -f "$tmp"

if [ $exit_status -ne 0 ]; then
echo "genCommitMsg: failed to generate commit message (exit code $exit_status)" >&2
printf '%s\n' "$output" >&2
return $exit_status
fi

echo "$output" | tail -n 1
local msg
msg="$(printf '%s\n' "$output" | sed '/^[[:space:]]*$/d' | tail -n 1 | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')"
if [ -z "$msg" ]; then
echo "genCommitMsg: model returned an empty commit message" >&2
return 1
fi
if printf '%s' "$msg" | grep -Eiq '(^|[^a-z])(error|exception|traceback|rate limit|429|failed|unauthorized|forbidden)([^a-z]|$)'; then
echo "genCommitMsg: refusing suspicious generated commit message: $msg" >&2
return 1
fi

echo "$msg"
return 0
}

# shellcheck disable=SC2016
add_alias gcoto-openai 'git commit -m "$(genCommitMsg openai-codex/gpt-5.4-mini)"'
# shellcheck disable=SC2016
add_alias gcoto-deepseek 'git commit -m "$(genCommitMsg deepseek/deepseek-v4-flash)"'
# shellcheck disable=SC2016
add_alias gcoto-free 'git commit -m "$(genCommitMsg opencode/mimo-v2.5-free)"'
gcoto-commit-with-model() {
local model="$1"
if [ -z "$model" ]; then
echo "usage: gcoto-commit-with-model <model>" >&2
return 1
fi

local msg
msg="$(genCommitMsg "$model")" || return 1
git commit -m "$msg"
}

gcoto-openai() {
gcoto-commit-with-model openai-codex/gpt-5.4-mini
}

gcoto-deepseek() {
gcoto-commit-with-model deepseek/deepseek-v4-flash
}

gcoto-free() {
gcoto-commit-with-model opencode/mimo-v2.5-free
}

# gcoto-model: model selection helper (cached per session via _GCOTO_MODEL)
# Usage: gcoto-model [openai|deepseek|free|current|unset]
Expand Down Expand Up @@ -104,7 +144,7 @@ function gcoto {
echo "gcoto: no model selected" >&2
return 1
fi
git commit -m "$(genCommitMsg "$_GCOTO_MODEL")"
gcoto-commit-with-model "$_GCOTO_MODEL"
}

# --- gacp (add-commit-push, depends on gcoto and gpush/gaireview from 20-git.bash) ---
Expand Down
44 changes: 44 additions & 0 deletions tests/bashrc-smoke.sh
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,12 @@ _result=$(_run_interactive "$FIXTURES_DIR" '
declare -F gcoto >/dev/null && echo "__FN_gcoto__"
declare -F gcoto-model >/dev/null && echo "__FN_gcoto_model__"
declare -F gacp >/dev/null && echo "__FN_gacp__"
declare -F gcoto-commit-with-model >/dev/null && echo "__FN_gcoto_commit_with_model__"
declare -F gcoto-openai >/dev/null && echo "__FN_gcoto_openai__"
declare -F gcoto-deepseek >/dev/null && echo "__FN_gcoto_deepseek__"
declare -F gcoto-free >/dev/null && echo "__FN_gcoto_free__"
declare -F add_alias >/dev/null && echo "__FN_add_alias__"
declare -F jprofile_path_prepend >/dev/null && echo "__FN_jprofile_path_prepend__"

# Check key aliases
alias gpush >/dev/null 2>&1 && echo "__AL_gpush__"
Expand Down Expand Up @@ -111,7 +116,12 @@ assert_match "gtagpush exists" "__FN_gtagpush__" "$_result"
assert_match "gcoto exists" "__FN_gcoto__" "$_result"
assert_match "gcoto-model exists" "__FN_gcoto_model__" "$_result"
assert_match "gacp exists" "__FN_gacp__" "$_result"
assert_match "gcoto-commit-with-model exists" "__FN_gcoto_commit_with_model__" "$_result"
assert_match "gcoto-openai exists" "__FN_gcoto_openai__" "$_result"
assert_match "gcoto-deepseek exists" "__FN_gcoto_deepseek__" "$_result"
assert_match "gcoto-free exists" "__FN_gcoto_free__" "$_result"
assert_match "add_alias exists" "__FN_add_alias__" "$_result"
assert_match "jprofile_path_prepend exists" "__FN_jprofile_path_prepend__" "$_result"

assert_match "gpush alias" "__AL_gpush__" "$_result"
assert_match "gs alias" "__AL_gs__" "$_result"
Expand Down Expand Up @@ -140,9 +150,16 @@ _result=$(_run_interactive "" '

count=$(printf "%s" "$PROMPT_COMMAND" | grep -o "jprofile_prompt_hook" | wc -l)
echo "HOOK_COUNT=$count"

mkdir -p "$HOME/bin"
jprofile_path_prepend "$HOME/bin"
jprofile_path_prepend "$HOME/bin"
path_count=$(printf "%s" "$PATH" | tr : "\n" | grep -c "^$HOME/bin$")
echo "PATH_COUNT=$path_count"
')

assert_match "single hook after re-source" "HOOK_COUNT=1" "$_result"
assert_match "path prepend is idempotent" "PATH_COUNT=1" "$_result"

# ---------------------------------------------------------------------------
# Scenario 4 - Helper command sanity
Expand Down Expand Up @@ -201,6 +218,33 @@ _result=$(_run_interactive "" '
')
assert_match "gom outside git repo returns error" "GOM_NO_GIT_OK" "$_result"

# Test gch picker propagates fzf failures in non-interactive contexts
_result=$(_run_interactive "" '
echo "source '"$LOADER"'" > "$HOME/.bashrc"
source "$HOME/.bashrc"
if gch 2>&1; then
echo "GCH_PICKER_FAIL"
else
echo "GCH_PICKER_OK"
fi
')
assert_match "gch picker failure returns error" "GCH_PICKER_OK" "$_result"

# Test genCommitMsg refuses to run without staged changes before invoking an agent
_result=$(_run_interactive "" '
echo "source '"$LOADER"'" > "$HOME/.bashrc"
source "$HOME/.bashrc"
mkdir "$HOME/repo"
cd "$HOME/repo" || exit 1
git init >/dev/null 2>&1
if genCommitMsg openai-codex/gpt-5.4-mini 2>&1; then
echo "GEN_NO_STAGED_FAIL"
else
echo "GEN_NO_STAGED_OK"
fi
')
assert_match "genCommitMsg without staged changes returns error" "GEN_NO_STAGED_OK" "$_result"

# Test klogs_deploy usage
_result=$(_run_interactive "$FIXTURES_DIR" '
echo "source '"$LOADER"'" > "$HOME/.bashrc"
Expand Down
Loading