gitbasher 5.2.0 → 5.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/gitb +61 -10
  3. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -2,6 +2,12 @@
2
2
 
3
3
  All notable changes to gitbasher are generated automatically by [semantic-release](https://github.com/semantic-release/semantic-release) from [Conventional Commits](https://www.conventionalcommits.org/) on `main`. The project follows [Semantic Versioning](https://semver.org/).
4
4
 
5
+ ## [5.2.1](https://github.com/maxbolgarin/gitbasher/compare/v5.2.0...v5.2.1) (2026-07-11)
6
+
7
+ ### 🐛 Bug Fixes
8
+
9
+ * **commit:** gate AI split by scope count and stop discarding near-complete groupings ([90becec](https://github.com/maxbolgarin/gitbasher/commit/90becec0f49a7a3e57e57e11a71b6909884ab337))
10
+
5
11
  ## [5.2.0](https://github.com/maxbolgarin/gitbasher/compare/v5.1.0...v5.2.0) (2026-07-07)
6
12
 
7
13
  ### 🚀 Features
package/dist/gitb CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env bash
2
2
  set -o pipefail
3
- GITBASHER_VERSION="5.2.0"
3
+ GITBASHER_VERSION="5.2.1"
4
4
  if [ "$1" == "init" ] || [ "$1" == "i" ]; then
5
5
  git init
6
6
  fi
@@ -6972,6 +6972,18 @@ function get_max_split_groups {
6972
6972
  [ "$v" -gt 20 ] && v=20
6973
6973
  printf '%s' "$v"
6974
6974
  }
6975
+ function should_attempt_ai_grouping {
6976
+ local scope_count="$1" mode="$2"
6977
+ case "$mode" in
6978
+ always) return 0 ;;
6979
+ never) return 1 ;;
6980
+ esac
6981
+ case "$scope_count" in
6982
+ ''|*[!0-9]*) return 1 ;;
6983
+ esac
6984
+ [ "$scope_count" -ge 3 ] && return 0
6985
+ return 1
6986
+ }
6975
6987
  #
6976
6988
  #
6977
6989
  #
@@ -7146,6 +7158,7 @@ Read <diff> to decide which files implement the same feature. Output TSV (scope<
7146
7158
  local ai_response
7147
7159
  ai_response=$(call_ai_api "$system_prompt" "$user_prompt" "$AI_MAX_TOKENS_FULL" "" "" "low" 2>/dev/null)
7148
7160
  if [ $? -ne 0 ] || [ -z "$ai_response" ]; then
7161
+ ai_grouping_fail_reason="API error"
7149
7162
  return 1
7150
7163
  fi
7151
7164
  ai_response=$(printf '%s' "$ai_response" | LC_ALL=C sed -e 's/^```[a-zA-Z]*$//' -e 's/^```$//')
@@ -7153,6 +7166,14 @@ Read <diff> to decide which files implement the same feature. Output TSV (scope<
7153
7166
  while IFS= read -r f; do
7154
7167
  [ -n "$f" ] && gset_add staged_set "$f"
7155
7168
  done <<< "$staged_files"
7169
+ gmap_clear folder_scope_of
7170
+ local _fk _ff
7171
+ for _fk in "${split_group_keys[@]}"; do
7172
+ while IFS= read -r _ff; do
7173
+ [ -n "$_ff" ] && gmap_set folder_scope_of "$_ff" "$_fk"
7174
+ done <<< "$(gmap_get split_groups "$_fk")"
7175
+ done
7176
+ gmap_clear assigned_set
7156
7177
  gmap_clear new_groups
7157
7178
  local -a new_keys=()
7158
7179
  local line scope file tab_count
@@ -7174,6 +7195,8 @@ Read <diff> to decide which files implement the same feature. Output TSV (scope<
7174
7195
  fi
7175
7196
  scope=$(printf '%s' "$scope" | LC_ALL=C tr '[:upper:]' '[:lower:]')
7176
7197
  gset_has staged_set "$file" || continue
7198
+ gset_has assigned_set "$file" && continue
7199
+ gset_add assigned_set "$file"
7177
7200
  if ! gmap_has new_groups "$scope"; then
7178
7201
  new_keys+=("$scope")
7179
7202
  gmap_set new_groups "$scope" "$file"
@@ -7181,15 +7204,44 @@ Read <diff> to decide which files implement the same feature. Output TSV (scope<
7181
7204
  gmap_set new_groups "$scope" "$(gmap_get new_groups "$scope")"$'\n'"$file"
7182
7205
  fi
7183
7206
  done <<< "$ai_response"
7184
- local total_assigned=0 total_staged
7207
+ local total_assigned=0 total_staged unmatched
7185
7208
  local key
7186
7209
  for key in "${new_keys[@]}"; do
7187
7210
  total_assigned=$((total_assigned + $(printf '%s\n' "$(gmap_get new_groups "$key")" | grep -c .)))
7188
7211
  done
7189
7212
  total_staged=$(printf '%s\n' "$staged_files" | grep -c .)
7190
- if [ "$total_assigned" -ne "$total_staged" ] || [ ${#new_keys[@]} -lt 1 ]; then
7213
+ if [ ${#new_keys[@]} -lt 1 ] || [ "$total_assigned" -eq 0 ]; then
7214
+ ai_grouping_fail_reason="no usable grouping returned"
7215
+ return 1
7216
+ fi
7217
+ unmatched=$((total_staged - total_assigned))
7218
+ [ "$unmatched" -lt 0 ] && unmatched=0 # dedup should prevent this; be safe
7219
+ if [ "$unmatched" -gt 0 ] && [ $((unmatched * 5)) -gt "$total_staged" ]; then
7220
+ ai_grouping_fail_reason="${unmatched} of ${total_staged} files unassigned"
7191
7221
  return 1
7192
7222
  fi
7223
+ if [ "$unmatched" -gt 0 ]; then
7224
+ local single_group=""
7225
+ [ ${#new_keys[@]} -eq 1 ] && single_group="${new_keys[0]}"
7226
+ local sf leftover_scope
7227
+ while IFS= read -r sf; do
7228
+ [ -z "$sf" ] && continue
7229
+ gset_has assigned_set "$sf" && continue
7230
+ if [ -n "$single_group" ]; then
7231
+ leftover_scope="$single_group"
7232
+ else
7233
+ leftover_scope=$(gmap_get folder_scope_of "$sf")
7234
+ [ -z "$leftover_scope" ] && leftover_scope="misc"
7235
+ fi
7236
+ if ! gmap_has new_groups "$leftover_scope"; then
7237
+ new_keys+=("$leftover_scope")
7238
+ gmap_set new_groups "$leftover_scope" "$sf"
7239
+ else
7240
+ gmap_set new_groups "$leftover_scope" "$(gmap_get new_groups "$leftover_scope")"$'\n'"$sf"
7241
+ fi
7242
+ gset_add assigned_set "$sf"
7243
+ done <<< "$staged_files"
7244
+ fi
7193
7245
  gmap_clear split_groups
7194
7246
  split_group_keys=()
7195
7247
  for key in "${new_keys[@]}"; do
@@ -7606,18 +7658,17 @@ function try_offer_commit_split {
7606
7658
  local ai_grouping
7607
7659
  ai_grouping=$(get_config_value gitbasher.commit-ai-grouping "auto")
7608
7660
  local should_use_ai="false"
7609
- case "$ai_grouping" in
7610
- never) should_use_ai="false" ;;
7611
- *) should_use_ai="true" ;; # auto + always: attempt AI
7612
- esac
7613
- if [ -z "$llm" ]; then
7614
- should_use_ai="false"
7661
+ if [ -n "$llm" ] && should_attempt_ai_grouping "${#split_group_keys[@]}" "$ai_grouping"; then
7662
+ should_use_ai="true"
7615
7663
  fi
7616
7664
  if [ "$should_use_ai" = "true" ] && check_ai_available 2>/dev/null; then
7617
7665
  echo
7618
7666
  echo -e "${YELLOW}Grouping changes by feature with AI...${ENDCOLOR}"
7667
+ local ai_grouping_fail_reason=""
7619
7668
  if ! group_files_by_feature_with_ai; then
7620
- echo -e "${YELLOW}AI feature grouping failed, falling back to folder-based grouping.${ENDCOLOR}"
7669
+ local reason_suffix=""
7670
+ [ -n "$ai_grouping_fail_reason" ] && reason_suffix=" (${ai_grouping_fail_reason})"
7671
+ echo -e "${YELLOW}AI feature grouping failed${reason_suffix}, using folder-based grouping.${ENDCOLOR}"
7621
7672
  fi
7622
7673
  consolidate_split_groups "$(get_max_split_groups)"
7623
7674
  fi
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gitbasher",
3
- "version": "5.2.0",
3
+ "version": "5.2.1",
4
4
  "description": "Simple bash utility that makes git easy to use",
5
5
  "keywords": [
6
6
  "git",