Skip to content

Commit 7efabeb

Browse files
henrikjeclaude
andcommitted
fix(shell): add missing --continue/--abort completions and fix zsh empty-array error
Add --continue and --abort to shell completions for pull, rebase, merge, retarget, rename, and branch rename in both bash and zsh. Replace zsh ($array) completion actions with {compadd -a array} to prevent _tags:comptags errors when the array is empty. Remove spurious -h/--help from retarget and extract completions. Use skipCell() in branch-rename for consistency. Document test:pbt, test:perf, test:mutate in CLAUDE.md. Add classify-retarget unit tests. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent ce81b90 commit 7efabeb

5 files changed

Lines changed: 347 additions & 33 deletions

File tree

CLAUDE.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,9 @@ Build and release tooling. `set-version.ts` stamps `src/version.ts` at build tim
7878
| `bun run test:integration` | Build and run Bun integration tests |
7979
| `bun run build && bun test test/integration/sync.test.ts` | Run a single integration test file |
8080
| `bun run test:integration:git217` | Build Docker image with git 2.17 and run integration tests |
81+
| `bun run test:pbt` | Build and run property-based tests (test/pbt/) |
82+
| `bun run test:perf` | Build and run performance benchmarks (test/perf/) |
83+
| `bun run test:mutate` | Run mutation testing with Stryker |
8184
| `bun run lint` | Check with Biome (formatting + linting) |
8285
| `bun run lint:fix` | Auto-fix lint/format issues |
8386
| `bun run typecheck` | TypeScript type checking |

shell/arb.bash

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -406,7 +406,7 @@ __arb_complete_rename() {
406406
return # branch name, no completion
407407
fi
408408
if [[ "$cur" == -* ]]; then
409-
COMPREPLY=($(compgen -W "--branch --base -r --delete-remote --fetch -N --no-fetch --dry-run -y --yes --include-in-progress" -- "$cur"))
409+
COMPREPLY=($(compgen -W "--branch --base -r --delete-remote --fetch -N --no-fetch --dry-run -y --yes --include-in-progress --continue --abort" -- "$cur"))
410410
return
411411
fi
412412
}
@@ -424,7 +424,7 @@ __arb_complete_branch() {
424424
done
425425

426426
local show_opts="-q --quiet -v --verbose --fetch -N --no-fetch --json --schema"
427-
local rename_opts="-r --delete-remote --fetch -N --no-fetch --dry-run -y --yes --include-in-progress"
427+
local rename_opts="-r --delete-remote --fetch -N --no-fetch --dry-run -y --yes --include-in-progress --continue --abort"
428428
local base_opts="--unset -f --force"
429429

430430
if ((COMP_CWORD == sub_pos)); then
@@ -477,7 +477,7 @@ __arb_complete_pull() {
477477
return
478478
fi
479479
if [[ "$cur" == -* ]]; then
480-
COMPREPLY=($(compgen -W "--reset -y --yes --dry-run -v --verbose --rebase --merge --autostash --include-wrong-branch -w --where" -- "$cur"))
480+
COMPREPLY=($(compgen -W "--reset -y --yes --dry-run -v --verbose --rebase --merge --autostash --include-wrong-branch -w --where --continue --abort" -- "$cur"))
481481
return
482482
fi
483483
COMPREPLY=($(compgen -W "$(__arb_workspace_repo_names "$base_dir")" -- "$cur"))
@@ -505,7 +505,7 @@ __arb_complete_rebase() {
505505
return
506506
fi
507507
if [[ "$cur" == -* ]]; then
508-
COMPREPLY=($(compgen -W "--fetch -N --no-fetch -y --yes --dry-run -v --verbose -g --graph --autostash --include-wrong-branch -w --where" -- "$cur"))
508+
COMPREPLY=($(compgen -W "--fetch -N --no-fetch -y --yes --dry-run -v --verbose -g --graph --autostash --include-wrong-branch -w --where --continue --abort" -- "$cur"))
509509
return
510510
fi
511511
COMPREPLY=($(compgen -W "$(__arb_workspace_repo_names "$base_dir")" -- "$cur"))
@@ -514,7 +514,7 @@ __arb_complete_rebase() {
514514
__arb_complete_retarget() {
515515
local base_dir="$1" cur="$2"
516516
if [[ "$cur" == -* ]]; then
517-
COMPREPLY=($(compgen -W "--fetch -N --no-fetch -y --yes --dry-run -v --verbose -g --graph --autostash --include-wrong-branch -h --help" -- "$cur"))
517+
COMPREPLY=($(compgen -W "--fetch -N --no-fetch -y --yes --dry-run -v --verbose -g --graph --autostash --include-wrong-branch --continue --abort" -- "$cur"))
518518
return
519519
fi
520520
}
@@ -527,7 +527,7 @@ __arb_complete_merge() {
527527
return
528528
fi
529529
if [[ "$cur" == -* ]]; then
530-
COMPREPLY=($(compgen -W "--fetch -N --no-fetch -y --yes --dry-run -v --verbose -g --graph --autostash --include-wrong-branch -w --where" -- "$cur"))
530+
COMPREPLY=($(compgen -W "--fetch -N --no-fetch -y --yes --dry-run -v --verbose -g --graph --autostash --include-wrong-branch -w --where --continue --abort" -- "$cur"))
531531
return
532532
fi
533533
COMPREPLY=($(compgen -W "$(__arb_workspace_repo_names "$base_dir")" -- "$cur"))

shell/arb.zsh

Lines changed: 33 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@ _arb() {
212212
case "${words[1]}" in
213213
delete)
214214
_arguments \
215-
'*:workspace:($ws_names)' \
215+
'*:workspace:{compadd -a ws_names}' \
216216
'(-f --force)'{-f,--force}'[Force removal]' \
217217
'(-r --delete-remote)'{-r,--delete-remote}'[Delete remote branches]' \
218218
'(-y --yes)'{-y,--yes}'[Skip confirmation prompt]' \
@@ -234,6 +234,8 @@ _arb() {
234234
'--dry-run[Show what would happen without executing]' \
235235
'(-y --yes)'{-y,--yes}'[Skip confirmation prompt]' \
236236
'--include-in-progress[Rename repos even if they have an in-progress git operation]' \
237+
'--continue[Resume a partial workspace rename]' \
238+
'--abort[Cancel the in-progress rename and restore state]' \
237239
'1:new-name:'
238240
;;
239241
path)
@@ -255,7 +257,7 @@ _arb() {
255257
compadd -a git_wt
256258
compadd -S '/' -a ws_names
257259
else
258-
_arguments '1:workspace:($ws_names)'
260+
_arguments '1:workspace:{compadd -a ws_names}'
259261
fi
260262
;;
261263
cd)
@@ -289,7 +291,7 @@ _arb() {
289291
compadd -S '/' -a ws_names
290292
else
291293
# Before slash: complete workspace names
292-
_arguments '1:workspace:($ws_names)'
294+
_arguments '1:workspace:{compadd -a ws_names}'
293295
fi
294296
fi
295297
;;
@@ -302,14 +304,14 @@ _arb() {
302304
'(-N --fetch --no-fetch)--fetch[Fetch before creating (default)]' \
303305
'(-N --fetch --no-fetch)'{-N,--no-fetch}'[Skip pre-fetch]' \
304306
'1:name:' \
305-
'*:repo:($repo_names)'
307+
'*:repo:{compadd -a repo_names}'
306308
;;
307309
attach)
308310
_arguments \
309311
'(-a --all-repos)'{-a,--all-repos}'[Add all remaining repos]' \
310312
'(-N --fetch --no-fetch)--fetch[Fetch before attaching (default)]' \
311313
'(-N --fetch --no-fetch)'{-N,--no-fetch}'[Skip pre-fetch]' \
312-
'*:repo:($repo_names)'
314+
'*:repo:{compadd -a repo_names}'
313315
;;
314316
detach)
315317
_arguments \
@@ -320,7 +322,7 @@ _arb() {
320322
'--dry-run[Show what would happen without executing]' \
321323
'(-N --fetch --no-fetch)--fetch[Fetch before detaching (default)]' \
322324
'(-N --fetch --no-fetch)'{-N,--no-fetch}'[Skip pre-fetch]' \
323-
'*:repo:($ws_repo_names)'
325+
'*:repo:{compadd -a ws_repo_names}'
324326
;;
325327
repo)
326328
shift words; (( CURRENT-- ))
@@ -347,7 +349,7 @@ _arb() {
347349
'(-a --all-repos)'{-a,--all-repos}'[Remove all canonical repos]' \
348350
'(-y --yes)'{-y,--yes}'[Skip confirmation prompt]' \
349351
'--dry-run[Show what would be removed without removing]' \
350-
'*:repo:($repo_names)'
352+
'*:repo:{compadd -a repo_names}'
351353
;;
352354
list)
353355
shift words; (( CURRENT-- ))
@@ -361,7 +363,7 @@ _arb() {
361363
shift words; (( CURRENT-- ))
362364
_arguments \
363365
'--remove[Remove repos from defaults]' \
364-
'*:repo:($repo_names)'
366+
'*:repo:{compadd -a repo_names}'
365367
;;
366368
esac
367369
fi
@@ -392,7 +394,7 @@ _arb() {
392394
'(-q --quiet --json -v --verbose --schema)'{-q,--quiet}'[Output one repo name per line]' \
393395
'(--json -q --quiet --schema)--json[Output structured JSON]' \
394396
'(--schema --json -q --quiet -v --verbose)--schema[Print JSON Schema for --json output]' \
395-
'*:repo:($ws_repo_names)'
397+
'*:repo:{compadd -a ws_repo_names}'
396398
;;
397399
watch)
398400
_arguments \
@@ -436,6 +438,8 @@ _arb() {
436438
'--dry-run[Show what would happen without executing]' \
437439
'(-y --yes)'{-y,--yes}'[Skip confirmation prompt]' \
438440
'--include-in-progress[Rename repos even if they have an in-progress git operation]' \
441+
'--continue[Resume a partial branch rename]' \
442+
'--abort[Cancel the in-progress branch rename and restore state]' \
439443
'1:new-name:'
440444
;;
441445
base)
@@ -450,15 +454,15 @@ _arb() {
450454
;;
451455
exec)
452456
_arguments \
453-
'*--repo[Only run in specified repos]:repo:($ws_repo_names)' \
457+
'*--repo[Only run in specified repos]:repo:{compadd -a ws_repo_names}' \
454458
'(-d --dirty -w --where)'{-d,--dirty}'[Only run in dirty repos]' \
455459
'(-d --dirty -w --where)'{-w,--where}'[Filter repos by status flags]:filter:_arb_where_filter' \
456460
'(-p --parallel)'{-p,--parallel}'[Run concurrently across repos]' \
457461
'*:command:'
458462
;;
459463
open)
460464
_arguments \
461-
'*--repo[Only open specified repos]:repo:($ws_repo_names)' \
465+
'*--repo[Only open specified repos]:repo:{compadd -a ws_repo_names}' \
462466
'(-d --dirty -w --where)'{-d,--dirty}'[Only open dirty worktrees]' \
463467
'(-d --dirty -w --where)'{-w,--where}'[Filter worktrees by status flags]:filter:_arb_where_filter' \
464468
'1:editor:(code cursor zed subl)'
@@ -474,7 +478,9 @@ _arb() {
474478
'--autostash[Stash uncommitted changes before pull, re-apply after]' \
475479
'--include-wrong-branch[Include repos on a different branch than the workspace]' \
476480
'(-w --where)'{-w,--where}'[Filter repos by status flags]:filter:_arb_where_filter' \
477-
'*:repo:($ws_repo_names)'
481+
'--continue[Resume after resolving conflicts]' \
482+
'--abort[Cancel the in-progress pull and restore state]' \
483+
'*:repo:{compadd -a ws_repo_names}'
478484
;;
479485
push)
480486
_arguments \
@@ -487,7 +493,7 @@ _arb() {
487493
'--dry-run[Show what would happen without executing]' \
488494
'(-v --verbose)'{-v,--verbose}'[Show outgoing commits in the plan]' \
489495
'(-w --where)'{-w,--where}'[Filter repos by status flags]:filter:_arb_where_filter' \
490-
'*:repo:($ws_repo_names)'
496+
'*:repo:{compadd -a ws_repo_names}'
491497
;;
492498
rebase)
493499
_arguments \
@@ -500,7 +506,9 @@ _arb() {
500506
'--autostash[Stash uncommitted changes before rebase, re-apply after]' \
501507
'--include-wrong-branch[Include repos on a different branch than the workspace]' \
502508
'(-w --where)'{-w,--where}'[Filter repos by status flags]:filter:_arb_where_filter' \
503-
'*:repo:($ws_repo_names)'
509+
'--continue[Resume after resolving conflicts]' \
510+
'--abort[Cancel the in-progress rebase and restore state]' \
511+
'*:repo:{compadd -a ws_repo_names}'
504512
;;
505513
retarget)
506514
_arguments \
@@ -512,7 +520,8 @@ _arb() {
512520
'(-g --graph)'{-g,--graph}'[Show branch divergence graph in the plan]' \
513521
'--autostash[Stash uncommitted changes before retarget, re-apply after]' \
514522
'--include-wrong-branch[Include repos on a different branch than the workspace]' \
515-
'(-h --help)'{-h,--help}'[Show help]' \
523+
'--continue[Resume after resolving conflicts]' \
524+
'--abort[Cancel the in-progress retarget and restore state]' \
516525
'1:branch:'
517526
;;
518527
extract)
@@ -529,7 +538,6 @@ _arb() {
529538
'--include-wrong-branch[Include repos on a different branch than the workspace]' \
530539
'--continue[Resume after resolving conflicts]' \
531540
'--abort[Cancel in-progress extract and restore state]' \
532-
'(-h --help)'{-h,--help}'[Show help]' \
533541
'1:workspace:'
534542
;;
535543
merge)
@@ -543,7 +551,9 @@ _arb() {
543551
'--autostash[Stash uncommitted changes before merge, re-apply after]' \
544552
'--include-wrong-branch[Include repos on a different branch than the workspace]' \
545553
'(-w --where)'{-w,--where}'[Filter repos by status flags]:filter:_arb_where_filter' \
546-
'*:repo:($ws_repo_names)'
554+
'--continue[Resume after resolving conflicts]' \
555+
'--abort[Cancel the in-progress merge and restore state]' \
556+
'*:repo:{compadd -a ws_repo_names}'
547557
;;
548558
reset)
549559
_arguments \
@@ -558,7 +568,7 @@ _arb() {
558568
'(-v --verbose)'{-v,--verbose}'[Show commits to be reset in the plan]' \
559569
'--include-wrong-branch[Include repos on a different branch than the workspace]' \
560570
'(-w --where)'{-w,--where}'[Filter repos by status flags]:filter:_arb_where_filter' \
561-
'*:repo:($ws_repo_names)'
571+
'*:repo:{compadd -a ws_repo_names}'
562572
;;
563573
undo)
564574
_arguments \
@@ -567,7 +577,7 @@ _arb() {
567577
'(-v --verbose)'{-v,--verbose}'[Show commits being rolled back]' \
568578
'(-f --force)'{-f,--force}'[Force undo even when repos have drifted]' \
569579
'--discard[Delete corrupted operation record without undo]' \
570-
'*:repo:($ws_repo_names)'
580+
'*:repo:{compadd -a ws_repo_names}'
571581
;;
572582
log)
573583
_arguments \
@@ -579,7 +589,7 @@ _arb() {
579589
'(--schema --json)--schema[Print JSON Schema for --json output]' \
580590
'(-d --dirty -w --where)'{-d,--dirty}'[Only log dirty repos]' \
581591
'(-d --dirty -w --where)'{-w,--where}'[Filter repos by status flags]:filter:_arb_where_filter' \
582-
'*:repo:($ws_repo_names)'
592+
'*:repo:{compadd -a ws_repo_names}'
583593
;;
584594
help)
585595
local -a help_completions=(
@@ -629,7 +639,7 @@ _arb() {
629639
add)
630640
shift words; (( CURRENT-- ))
631641
_arguments \
632-
'*--repo[Target repo scope]:repo:($repo_names)' \
642+
'*--repo[Target repo scope]:repo:{compadd -a repo_names}' \
633643
'--workspace[Target workspace scope]' \
634644
'(-f --force)'{-f,--force}'[Overwrite existing template]' \
635645
'1:file:_files'
@@ -638,14 +648,14 @@ _arb() {
638648
diff)
639649
shift words; (( CURRENT-- ))
640650
_arguments \
641-
'*--repo[Filter to specific repo]:repo:($repo_names)' \
651+
'*--repo[Filter to specific repo]:repo:{compadd -a repo_names}' \
642652
'--workspace[Filter to workspace templates only]' \
643653
'1:template:{ _arb_template_names "$base_dir" }'
644654
;;
645655
apply)
646656
shift words; (( CURRENT-- ))
647657
_arguments \
648-
'*--repo[Apply only to specific repo]:repo:($repo_names)' \
658+
'*--repo[Apply only to specific repo]:repo:{compadd -a repo_names}' \
649659
'--workspace[Apply only workspace templates]' \
650660
'(-f --force)'{-f,--force}'[Overwrite drifted files]' \
651661
'--dry-run[Show what would happen without executing]' \

src/commands/branch-rename.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import {
2828
} from "../lib/git";
2929
import { type RenderContext, finishSummary, render } from "../lib/render";
3030
import type { Cell, OutputNode } from "../lib/render";
31-
import { EMPTY_CELL, cell, suffix } from "../lib/render";
31+
import { EMPTY_CELL, cell, skipCell, suffix } from "../lib/render";
3232
import { confirmOrExit, resolveDefaultFetch, runPlanFlow } from "../lib/sync";
3333
import {
3434
dryRunNotice,
@@ -232,15 +232,15 @@ export function buildRenamePlanNodes(
232232
remoteCell = a.shareRemote ? cell("no remote branch") : EMPTY_CELL;
233233
break;
234234
case "skip-missing":
235-
localCell = cell("skip — branch not found", "attention");
235+
localCell = skipCell("branch not found");
236236
remoteCell = a.shareRemote ? cell("no remote branch") : EMPTY_CELL;
237237
break;
238238
case "skip-wrong-branch":
239-
localCell = cell(`skip — on branch ${a.currentBranch ?? "?"}, expected ${oldBranch}`, "attention");
239+
localCell = skipCell(`on branch ${a.currentBranch ?? "?"}, expected ${oldBranch}`);
240240
remoteCell = a.shareRemote ? cell("no remote branch") : EMPTY_CELL;
241241
break;
242242
case "skip-in-progress":
243-
localCell = cell(`skip — ${a.operationType} in progress (use --include-in-progress)`, "attention");
243+
localCell = skipCell(`${a.operationType} in progress (use --include-in-progress)`);
244244
remoteCell = a.shareRemote ? cell("no remote branch") : EMPTY_CELL;
245245
break;
246246
default:

0 commit comments

Comments
 (0)